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