Google закрыл RCE в DevSite: ключ подписи Flask-сессий лежал прямо в cookie

7 октября 2026 года Google закрыл критическую уязвимость в DevSite — общей платформе документации, на которой работают developers.google.com, firebase.google.com, developer.android.com и source.android.com. OAuth client_secret одновременно лежал в Flask-cookie и служил ключом подписи сессий, поэтому любой посетитель мог подделать сессию с pickle-нагрузкой и выполнить код на сервере без входа.
Ошибку нашёл инженер Simon Köck и получил за неё $90 000 по программе Google Vulnerability Reward Program. Сама находка — не экзотика уровня Google, а сочетание трёх типовых решений, которые встречаются в обычных Python-проектах: секрет в клиентском хранилище, переиспользование одного ключа для разных задач и десериализация недоверенных данных. Именно такие связки ищет аудит кода приложения, и именно их не видит сканер зависимостей.
Ниже — как была устроена цепочка, почему исправление заняло два месяца и что проверить в своих Flask- и Django-сервисах. Общие принципы защиты сессий мы разбирали в материале о безопасной аутентификации в веб-приложениях.
Как работала цепочка от cookie до выполнения кода
По разбору исследователя, атака складывалась из пяти шагов, и ни один из них не требовал учётной записи Google:
- запрос к открытому эндпоинту авторизации OAuth на developers.google.com возвращал Flask-cookie сессии — заметно больше, чем нужно для CSRF-токена и адреса возврата;
- после base64-декодирования и распаковки zlib внутри обнаруживались сериализованный через pickle объект OAuth-потока и пара client_id / client_secret в открытом виде;
- тот же client_secret использовался как Flask SECRET_KEY: подпись, пересчитанная офлайн этим значением, побайтно совпала с подписью на cookie;
- зная ключ, можно было записать в сессию любые данные и получить подпись, которую сервер принимал как свою;
- OAuth-callback вызывал pickle.loads() для объекта из сессии, и подменённая нагрузка исполнялась при десериализации.
Выполнение кода исследователь подтвердил безопасно — нагрузкой с time.sleep(): задержка ответа сервера совпадала с заданной.
Какие сайты Google были затронуты
DevSite — общая инфраструктура, поэтому один ключ подписывал сессии сразу на нескольких доменах. В отчёте перечислены developers.google.com, firebase.google.com, developer.android.com, source.android.com и docs.partner.android.com; The New Stack говорит о семи доменах Google с тем же ключом. Это витрина документации, через которую ежедневно проходят миллионы разработчиков, — то есть потенциальная точка для подмены инструкций и кода примеров.
Хронология: от отчёта до исправления
Отчёт отправлен 6 августа 2026 года, на следующий день Google его проверил и принял. 27 августа определена награда: по данным The New Stack, базовые $75 000 за уязвимость высшей категории S0 в приложении обычного уровня, умноженные на 1,2 за качество отчёта. Исправление вышло только 7 октября — через два месяца после того, как уязвимость была подтверждена. Признаков эксплуатации в публикациях нет.
Что это значит для команд разработки
Подпись Flask-сессии защищает от подделки, но не от чтения: содержимое стандартной cookie-сессии любой может декодировать. Всё, что туда положено, нужно считать публичным. Ключ подписи, попавший к клиенту, обнуляет и эту защиту. А pickle в цепочке превращает подделку данных в выполнение кода — документация Python прямо предупреждает, что распаковывать pickle можно только из полностью доверенного источника.
Отдельный урок — переиспользование секретов. OAuth client_secret и ключ подписи сессий решают разные задачи, и утечка одного не должна автоматически компрометировать другое.
Что проверить в своих Python-сервисах
1. Найдите все вызовы pickle.loads, pickle.load и обёрток над ними (в том числе в сторонних библиотеках сессий и кэшей) и убедитесь, что данные приходят не от клиента. Для сессий и обмена — JSON со схемой валидации.
2. Проверьте, что SECRET_KEY уникален, задан на сервере, хранится в менеджере секретов и не совпадает ни с одним другим ключом или токеном.
3. Декодируйте свои cookie сессий и посмотрите, что в них лежит. Токены, секреты и объекты процессов — на сервер, в хранилище сессий; в cookie — только идентификатор.
4. Если что-то из этого найдено — ротируйте ключи, а не только правьте код: старые подписанные сессии и утёкшие секреты продолжат работать.
Часто задаваемые вопросы
Нужно ли что-то делать пользователям сайтов Google? Нет. Исправление выполнено на стороне Google 7 октября 2026 года, данных об эксплуатации нет.
Это уязвимость во Flask? Нет. Flask работает как задумано: подписанная cookie-сессия читаема клиентом по определению. Ошибка — в том, что положили в сессию и каким ключом её подписали.
Почему pickle так опасен? При распаковке pickle может вызвать произвольную функцию — это заложено в формат. Любые данные, которые мог изменить клиент, превращаются в код.
Есть ли у уязвимости CVE? В опубликованных материалах идентификатор CVE не указан: ошибка была в собственном сервисе Google, а не в распространяемом ПО.
Источники
Simon Köck — How I Found a $90,000 Remote Code Execution Vulnerability in Google (первоисточник, технический разбор и хронология).
The New Stack — Google paid $90,000 for this bug, then waited two months to fix it.
Документация Python — модуль pickle (предупреждение о десериализации недоверенных данных).
Найдём небезопасную десериализацию до того, как её найдут за вас
Проверим код Python-сервисов на pickle и опасные загрузчики, разберём хранение секретов и сессий, проверим переиспользование ключей и дадим план исправлений с приоритетами.