Сайты технической документации Google работают на общей платформе DevSite. Ошибка в её конфигурации позволяла любому человеку без учётной записи Google подделать сессионную cookie и заставить сервер выполнить произвольный код. Затронуты developers.google.com, firebase.google.com, developer.android.com, source.android.com и docs.partner.android.com, а один и тот же ключ подписи действовал на семи доменах. Google исправила проблему 7 октября 2026 года, а исследователь Саймон Кёк получил 90 000 долларов по программе вознаграждения за уязвимости.
Как устроена уязвимость в DevSite
Кёк разбирал процесс авторизации OAuth (протокол, по которому сервис получает доступ к данным пользователя с его согласия), общий для документации Google. Запрос к точке авторизации не требовал входа в аккаунт, зато возвращал сессионную cookie. Она оказалась размером около 1200 байт, что намного больше обычного набора из токена защиты от межсайтовой подделки запросов и адреса возврата.
Формат сессии во Flask состоит из трёх частей: полезной нагрузки, отметки времени и подписи. Подпись подтверждает целостность, но не скрывает содержимое. Любой, кто получил cookie, читает её данные без всяких усилий.
Внутри лежал объект OAuth-потока, сериализованный модулем pickle (формат хранения объектов Python) и закодированный base64. Среди полей оказались идентификатор клиента OAuth и его секрет.
Один секрет вместо двух
Секрет клиента совпадал со значением, которым подписывались Flask-сессии. Кёк проверил догадку расчётом: он воспроизвёл алгоритм выработки подписи и получил ту же строку байт в байт. Проще говоря, ключ хранился внутри объекта, который сам же и подписывал.
Отсюда следует главное. Злоумышленник мог собрать сессию с произвольным содержимым и выдать к ней правильную подпись. Приложение не отличало cookie, выданную Google, от подделки. Разница между доверенными и чужими данными исчезала полностью.
От подделки к выполнению кода
Обработчик обратного вызова извлекал объект потока из сессии, декодировал его и передавал в десериализацию pickle. Этот модуль опасен для данных извне, поскольку при разборе вызывает функции Python. Обычно защитой служит подпись cookie, но она опирается на секрет, а секрет лежал в открытом виде в той же cookie.
Кёк не стал запускать команды на рабочих серверах Google. Вместо этого он отправил поддельную сессию с полезной нагрузкой, которая приостанавливает работу. Задержки ответа совпадали с заданными значениями: около трёх, пяти и восьми секунд. Сервер действительно исполнял присланную функцию.
Контрольные проверки подтвердили картину. Запрос без cookie отклонялся; запрос с изменённым символом в подписи отклонялся; корректная подпись без записи потока давала ошибку состояния. Настоящий поток доходил до обмена токенами с Google и падал позже, на поддельном коде авторизации. Задержка срабатывала ровно на выбранное число секунд.
Масштаб и реакция Google
Секрет с developers.google.com подписывал действительные сессии и на других ресурсах: firebase.google.com, developer.android.com, source.android.com, docs.partner.android.com. Всего семь доменов. Причина в общей инфраструктуре DevSite, где настройки подписи не разделялись между площадками.
Атака не требовала аккаунта и обходилась двумя запросами. Первый возвращал cookie с секретом, второй приносил поддельную сессию. Утечка секрета, повторное использование ключа, хранение состояния на стороне клиента и небезопасная десериализация складывались в цепочку выполнения произвольного кода без аутентификации. По внутренней классификации Google такой сценарий относят к высшей категории угрозы.
Хронология выглядит так. Шестого августа 2026 года Кёк отправил отчёт. Седьмого августа его разобрали и приняли в тот же день. Двадцать седьмого августа определили награду: 75 000 долларов базовой ставки и повышающий коэффициент 1,2 за качество отчёта, итого 90 000. Исправление развернули седьмого октября 2026 года.
Что делать разработчикам
Google уже закрыла дыру на своей стороне, поэтому владельцам сайтов документации ничего предпринимать не нужно. Полезные выводы касаются всех, кто строит приложения на Flask. Ключ подписи сессий держат отдельно от учётных данных OAuth и хранят на сервере, а не внутри подписываемого объекта. При подозрении на утечку ключ меняют и аннулируют все выданные сессии. Объект авторизационного потока оставляют на сервере, а в cookie кладут только непрозрачную ссылку. Для данных, пересекающих границу с клиентом, вместо pickle подходит формат, который умеет хранить лишь простые значения и проверяется по схеме.
Ни одна из четырёх ошибок по отдельности не давала полного доступа к серверам Google. Заработали они вместе, и именно так обычно и возникают серьёзные цепочки: слабое место в конфигурации усиливает слабое место в обработке данных. Разделение секретов и отказ от опасной десериализации на границе доверия ломают подобные сценарии на раннем шаге.
Ссылки