Два пользователя поочерёдно запрашивают одну защищённую страницу через общий прокси. Как установить, не отдаёт ли прокси ответ первого пользователя второму?
Нужно выполнить один и тот же запрос от имени двух разных пользователей через общий кэш и проверить, может ли второй получить содержимое, сформированное для первого. Уязвимость подтверждается, если ответ второго пользователя содержит персональные данные первого или приходит из кэша без обращения к приложению.
Для защищённых персональных ответов обычно требуется директива Cache-Control: no-store. Директива private запрещает использование ответа общим кэшем, но допускает хранение в браузере; выбор зависит от чувствительности данных и требований к повторному просмотру.
HTTP-кэширование появилось для уменьшения задержки, нагрузки на серверы и объёма передаваемого трафика. Прокси и CDN могут повторно отдавать сохранённый ответ, не обращаясь к исходному приложению.
Изначально предполагалось, что одинаковый URL часто соответствует одинаковому публичному содержимому. Для персонализированных ответов это предположение неверно: один URL может возвращать разные данные в зависимости от сессии, роли или заголовков запроса.
Если общий кэш сохранит ответ авторизованного пользователя, следующий запрос к тому же ресурсу может получить этот ответ другой пользователь. В результате раскрываются профиль, документы, финансовые сведения или административные данные без обхода самой серверной авторизации.
Проблема особенно опасна, когда приложение корректно проверяет права, но после проверки формирует ответ, который промежуточный кэш считает общим. Тогда кэш фактически обходит прикладной контроль доступа.
Тест следует проводить минимум с двумя отдельными учётными записями и, по возможности, через тот же прокси или CDN, который используется в целевой среде. Сначала пользователь А запрашивает защищённый ресурс с уникальным маркером в своих данных, затем пользователь Б запрашивает тот же ресурс. Нужно сравнить тело ответа, заголовки, идентификаторы кэширования и факт обращения к приложению.
Положительный признак — пользователь Б получает маркер пользователя А, хотя серверная сессия Б не должна иметь к нему доступа. Дополнительные признаки — неожиданный заголовок попадания в кэш, отсутствие обращения к origin-серверу или одинаковый ответ при разных сессиях.
Для чувствительных ответов безопасная настройка обычно выглядит так:
no-store запрещает кэшам сохранять ответ. private ограничивает хранение браузером и не предназначен для полного запрета локального кэширования. no-cache не означает отсутствие хранения: он требует проверить свежесть перед повторным использованием, поэтому сам по себе не предотвращает сохранение чувствительного ответа.
Заголовок Vary может заставить кэш учитывать отдельные заголовки, например язык или тип устройства, но не является универсальной защитой персональных ответов. Вариация по cookie часто сложна, увеличивает количество вариантов и не заменяет запрет общего кэширования. Также нужно проверять не только HTML, но и JSON-ответы, ошибки, экспорт файлов и ответы CDN.
Тест должен учитывать, что браузерный кэш и общий прокси — разные уровни хранения. Очистка браузера не доказывает безопасность CDN, а отсутствие уязвимости на локальном стенде не гарантирует её отсутствия в рабочой цепочке прокси.
В приложении страница заказов имела одинаковый URL для всех пользователей. Сервер проверял сессию правильно, но CDN кэшировал успешный ответ по URL без учёта cookie.
Рассматривались три варианта. Учитывать cookie в ключе кэша можно было, но это создавало множество вариантов и повышало риск ошибки конфигурации. Использовать только private было недостаточно, поскольку требовалось исключить хранение на промежуточных узлах. Полностью отключить кэширование для страницы было проще и надёжнее, но увеличивало нагрузку.
Выбрали no-store для персональных ответов, а кэширование оставили только для явно публичных ресурсов. При проверке второй пользователь больше не получал данные первого; нагрузку снизили оптимизацией отдельных публичных запросов, а не ослаблением политики для защищённой страницы.
1. Достаточно ли проверить только тело ответа?
Нет. Нужно анализировать также заголовки, статус, признаки попадания в кэш и серверные журналы. Иногда утечка проявляется в JSON, скачиваемом файле или редиректе, а не в основной HTML-странице. Важно подтвердить именно повторное использование ответа, а не случайное совпадение данных.
2. Защищает ли наличие заголовка Authorization от утечки через кэш?
Не всегда. Поведение зависит от конкретного прокси, CDN и его настроек; полагаться только на предположение о том, что ответы с авторизацией никогда не кэшируются, нельзя. Политику нужно явно задать и проверить в фактической цепочке доставки, особенно если перед приложением есть несколько промежуточных компонентов.
3. Почему очистка кэша после выхода из системы не является полноценным решением?
Пользователь может выйти из системы, не имея возможности удалить копию ответа на общем прокси или CDN. Другой клиент способен запросить тот же ресурс и получить сохранённые данные независимо от состояния прежней сессии. Поэтому для чувствительных ответов предотвращать сохранение нужно до возникновения утечки, обычно через no-store, а не только удалять отдельные записи постфактум.