Переглянути джерело, скопіювати ключ, отримати все
Дослідник із безпеки відкрив джерело сторінки ClickUp, знайшов ключ API, зашитий у JavaScript, і використав його, щоб одним запитом отримати 959 адрес електронної пошти та 3 165 внутрішніх функціональних прапорців. Ключ не мав області доступу, обмеження частоти запитів і терміну дії.
Дослідник із безпеки зайшов на clickup.com. Відкрив джерело сторінки. Знайшов ключ API, зашитий у JavaScript. Скопіював його. Надіслав один GET-запит.
Отримав у відповідь 959 адрес електронної пошти та 3 165 внутрішніх функціональних прапорців. Співробітники Home Depot. Fortinet. Autodesk. Tenable. Rakuten. Mayo Clinic.
Один рядок. Один запит. Усе.
Як це стається
Комусь було потрібно, щоб фронтенд викликав API. API потребував автентифікації. Тому ключ внесли в JavaScript. Відправили в реліз, далі, наступний спринт.
Це не складна атака. Тут немає експлойту, немає zero-day, немає соціальної інженерії. Є view-source: і curl. Браузер і термінал. Те, що цікавий стажер робить у перший день.
Ключ не мав області доступу — він відкривав усе, що показував API. Не мав обмеження частоти запитів — один запит повернув усе. Не мав терміну дії — ключ працював, доки хтось не помітив. Не мав другого фактора — наявність рядка була єдиною перешкодою.
Питання грошей
Говорять про витік даних. Поговорімо про те, скільки ці дані коштують.
959 корпоративних адрес електронної пошти з компаній Fortune 500. Це список цілей для цілеспрямованого фішингу, за який зловмисники готові платити гроші. Імена, посади й факт того, що ці компанії користуються ClickUp, — це контекст для соціальної інженерії, який робить фішинг дієвим.
3 165 внутрішніх функціональних прапорців. Це дорожня карта. Вона показує конкурентам, що ClickUp розробляє, що тестує, що заховано за прапорцем. Вона показує зловмисникам, які функції наполовину готові, а отже, вразливі.
Це не інцидент із приватністю. Це витік комерційної інформації.
Чому це повторюється
Це вже четвертий інцидент із обліковими даними у вихідному коді, про який ми пишемо цього місяця. У Bitwarden облікові дані з CLI зібрали, бо вони лежали у файлах відкритим текстом. Змінні середовища Vercel можна було розшифрувати, бо ознака "sensitive" не була типовою. Розробник втратив 634 паролі з Chrome, бо ключ дешифрування лежав на тому самому диску.
Схема завжди однакова: облікові дані існують як рядок — у файлі, у змінній, у джерелі сторінки, — і щось їх зчитує. Те «щось» змінюється. Схема — ні.
Ключі API в JavaScript — найбільш кричущий випадок, бо атака взагалі не потрібна. Ключ опубліковано. Його отримує кожен відвідувач. Браузер завантажує його, відтворює та показує будь-кому, хто клацне правою кнопкою.
Що слід було зробити інакше
Виклик API ніколи не мав автентифікуватися статичним ключем із клієнтського боку. Варіанти:
- Проксі на бекенді. Фронтенд звертається до вашого бекенду, який зберігає ключ на сервері та проксює виклик API. Ключ ніколи не потрапляє в браузер.
- Токени в межах сесії. Після автентифікації фронтенд отримує токен із коротким терміном дії та вузькою областю доступу. Він має термін дії. Він робить лише те, що дозволено автентифікованому користувачеві. Це не мастер-ключ.
- Узагалі без ключа. Якщо дані публічні — віддавайте їх без автентифікації. Якщо ні — не віддавайте їх неавтентифікованому JavaScript.
Зашивати ключ API у клієнтському коді — це як покласти ключ від будинку під килимок і опублікувати свою адресу.
Проблема життєвого циклу облікових даних
Цей ключ ClickUp, імовірно, створили один раз, вставили у файл JavaScript, закомітили в репозиторій, розгорнули в продакшені — і більше ніколи про нього не думали. Ніхто його не ротував. Ніхто не обмежив область доступу. Ніхто не встановив термін дії. Ніхто не відстежував, до чого він має доступ.
Такий життєвий цикл більшості ключів API у більшості організацій. Створені поспіхом, вставлені туди, де потрібні, забуті. Вони накопичуються в кодових базах, конфігураційних файлах, конвеєрах CI/CD і, як бачимо, у джерелі сторінки — і кожен із них є дверима, які ніколи не замикаються.
Питання не в тому, чи є у вашій організації такий ключ. Питання в тому, скільки їх, і чи дізналися б Ви, якби хтось скопіював один із них сьогодні.