Подделка cookie без проверки подписи: как защитить веб-сессию
📋 Кратко
Cookie становится опасной точкой входа, когда приложение доверяет данным клиента без проверки целостности. Подмена роли, срока действия или идентификатора пользователя тогда превращается в обход авторизации. Разбираем разницу между случайным идентификатором сессии и подписанным состоянием, роль периметра, защиту от фиксации сессии и набор тестов для проверки.
Cookie часто выглядит как обычная строка в браузере. На деле она может определять, кто вошёл в систему и какие действия ему доступны.
Проблема возникает не из-за самого факта хранения cookie. Риск появляется, когда сервер принимает данные клиента без проверки их целостности. Пользователь тогда меняет роль, срок действия или идентификатор и отправляет запрос снова.
Периметр помогает заметить подозрительный трафик. Но он не заменяет проверку сессии внутри приложения. Решение о подлинности принимает серверная логика.
🔍 Где именно возникает подделка cookie
Cookie бывает указателем на серверную сессию. В этом случае браузер хранит только случайный идентификатор. Сервер по нему находит пользователя, его роль и другие параметры.
Другой вариант — cookie хранит состояние. В ней могут находиться идентификатор пользователя, срок действия, признак администратора или набор разрешений. Такая схема требует защиты целостности.
Целостность означает простой факт. Сервер понимает, что значение не изменили после выпуска. Подпись не скрывает содержимое cookie. Она только помогает обнаружить подмену.
Если приложение декодирует данные до проверки подписи, атакующий получает опасную возможность. Он меняет значение и рассчитывает на ошибку в порядке обработки.
Проверьте прямо сейчас, что именно хранит каждая служебная cookie. Отдельно отметьте идентификаторы, роли, права и сроки действия.
⚠️ Почему зелёный тест не доказывает защиту
Одна из проблем появляется в тестах на подделку. Проверка меняет первый символ всей строки cookie. После этого полезная часть может перестать разбираться.
Функция декодирования тогда возвращает ошибку. Но причина может быть любой: неверный формат, повреждённый JSON или неправильная подпись. Тест получает ожидаемое имя ошибки, хотя сравнение HMAC (кода проверки целостности) вообще отсутствует.
Такой тест создаёт ложное чувство безопасности. Код и проверку иногда пишет один и тот же ИИ-агент. Одинаковое ошибочное допущение попадает в оба места.
Надёжная проверка разделяет два сценария. В первом подпись меняется, а полезная нагрузка остаётся правильной. Во втором меняется полезная нагрузка, а старая подпись сохраняется.
Добавьте также тест на успешное чтение исходной cookie. Затем отдельно проверьте отказ при изменении каждого значимого поля.
Запустите тесты через команду, которая возвращает настоящий код завершения. Строка с последующей командой вывода может скрыть ошибку.
🔐 Как работает проверка подписи
Подписанная cookie обычно содержит полезную нагрузку и код проверки. Сервер выпускает код на основе секретного ключа и содержимого. При новом запросе сервер повторяет расчёт.
Если значения совпадают, сервер продолжает обработку. Если значения различаются, сервер сразу отклоняет cookie. Приложение не должно принимать роль или права из неподтверждённой нагрузки.
Сравнение подписи должно быть безопасным. Простое сравнение строк может раскрывать лишнюю информацию по времени ответа. Используйте проверку, рассчитанную на сравнение криптографических значений.
Ключ подписи нельзя хранить в коде или открытой конфигурации. Доступ к нему ограничивают. Изменение ключа должно иметь понятный план перехода.
При ротации выпускайте новые cookie новым ключом. Старый ключ можно принимать только ограниченное время. После завершения перехода его нужно отключить.
Сделайте сейчас отдельную процедуру смены ключа. В ней укажите срок перекрытия, порядок выпуска и реакцию на старую подпись.
🛡️ Почему серверная сессия остаётся базовым выбором
Серверная модель уменьшает объём доверия к браузеру. Cookie содержит только случайный идентификатор. Сервер сам хранит состояние и проверяет его при каждом обращении.
Роль пользователя не приходит из запроса как готовое решение. Сервер получает её из собственного хранилища. Поэтому изменение строки идентификатора не превращает обычного пользователя в администратора.
Такая модель упрощает отзыв сессии. Сервер удаляет запись или помечает её недействительной. Браузер может продолжать отправлять старую cookie, но приложение её больше не принимает.
Подписанное состояние тоже может быть оправдано. Однако разработчик должен строго определить состав данных. В cookie не стоит помещать лишние права и чувствительные сведения.
Шифрование решает другую задачу. Оно скрывает содержимое, но само по себе не заменяет проверку целостности. Для защиты от подмены серверу нужен механизм проверки.
Составьте перечень полей каждой cookie. Если поле не нужно клиенту, уберите его из браузера и перенесите на сервер.
⚙️ Какие атрибуты задаёт браузер
Атрибут Secure разрешает отправку cookie только через защищённое соединение. Это снижает риск перехвата при передаче. Для сессионных cookie такой атрибут должен быть обязательным.
Атрибут HttpOnly запрещает обычному сценарию страницы читать cookie. Он не блокирует все атаки, но уменьшает последствия попытки получить значение через сценарий.
SameSite задаёт правила отправки cookie при межсайтовых запросах. Это важно против CSRF (межсайтовой подделки запросов). Браузер автоматически отправляет некоторые маркеры входа, если запрос подходит под правила cookie.
CSRF использует доверие сайта к уже вошедшему браузеру. Вредоносная страница может попытаться отправить запрос в другой сервис. Сервер увидит действующую cookie и ошибочно примет запрос за действие пользователя.
Domain и Path ограничивают область отправки cookie. Слишком широкие значения увеличивают число мест, где браузер передаёт маркер. Для сессии выбирайте минимально необходимую область.
Срок действия также требует контроля. Сочетайте ограничение по времени с отзывом на сервере. Не оставляйте активную сессию без понятной причины.
Проверьте заголовок Set-Cookie на тестовом стенде. Убедитесь, что там есть Secure, HttpOnly и подходящий режим SameSite.
🎯 Как остановить фиксацию и повторное использование
Фиксация сессии начинается, когда приложение сохраняет прежний идентификатор после входа. Атакующий заранее добивается использования известного значения. После входа этот же идентификатор получает права пользователя.
Приложение должно менять идентификатор после успешного входа. То же правило действует после смены пароля и повышения привилегий. Старое значение нужно сделать недействительным.
Украденную cookie атакующий может повторно отправлять от своего имени. Сервер видит правильный маркер и считает запрос продолжением сессии. Поэтому одной защиты канала недостаточно.
Используйте два тайм-аута. Абсолютный ограничивает общую продолжительность сессии. Тайм-аут бездействия завершает её после периода отсутствия действий.
Пользователь должен иметь возможность отозвать все сессии. Такая функция важна после подозрения на кражу cookie. Она также помогает быстро закрыть доступ с потерянного устройства.
Дополнительные сигналы полезны, но не делайте жёсткую привязку только к IP-адресу. Адрес может измениться в обычной работе. Лучше учитывать сочетание признаков и требовать повторную проверку при аномалии.
Проведите этот сценарий на тестовой среде. Сохраните старую cookie и отправьте её после каждой операции.
🧱 Что делает периметр и чего он не делает
WAF (межсетевой экран веб-приложений) видит запросы на границе сервиса. Он может фильтровать аномальные шаблоны. Он также может ограничивать частоту запросов.
Периметр помогает обнаружить повторяющиеся попытки входа с разными значениями cookie. Он может передавать события в централизованное журналирование. Это облегчает поиск связанных запросов.
Но WAF не знает всех правил конкретной сессии. Он не должен решать, имеет ли пользователь право менять роль или просматривать данные. Такое решение остаётся задачей приложения.
Даже идеально настроенный фильтр не исправляет код, который принимает неподписанное состояние. Атакующий может отправить корректный по форме запрос. Проблема проявится только внутри сервиса.
Периметр также не отменяет отзыв сессий. После кражи cookie фильтр может не отличить владельца от злоумышленника. Сервер должен уметь закрыть маркер.
Настройте на границе журналирование отказов по cookie. Не записывайте в журналы полный секрет или значение маркера.
📋 Как строится проверка в приложении
Сначала приложение принимает cookie как недоверенный ввод. Оно проверяет формат и размер. Затем оно проверяет подпись или ищет идентификатор на сервере.
При неверной подписи приложение не использует полезную нагрузку. Оно завершает проверку и требует новый вход. Нельзя сначала прочитать роль, а потом решить, доверять ли cookie.
Проверка должна одинаково работать на всех маршрутах. Ошибка часто появляется не в основном входе, а в отдельном обработчике. Особенно опасны административные пути и внутренние API.
Для каждой операции определите, какие данные нужны серверу. Если действие меняет состояние, добавьте защиту от CSRF. Cookie не должна быть единственным доказательством намерения пользователя.
Журналируйте факт отказа, причину и контекст запроса. Не сохраняйте подпись, полный маркер или чувствительную нагрузку. События должны помогать расследованию, но не становиться новым источником утечки.
Составьте карту маршрутов, которые читают cookie. Затем проверьте каждый маршрут одинаковым набором негативных тестов.
🧪 Какие тесты выявляют реальную проблему
Первый тест меняет только полезную нагрузку. Подпись остаётся прежней. Ожидаемый результат — отказ до использования изменённых данных.
Второй тест меняет только подпись. Полезная нагрузка остаётся корректной. Ожидаемый результат — тот же отказ по целостности.
Третий тест удаляет подпись. Сервер не должен переходить к разбору роли или идентификатора. Он должен обработать cookie как недействительную.
Четвёртый тест меняет алгоритм или его обозначение. Сервер не должен принимать неожиданный режим проверки. Набор допустимых алгоритмов задаётся заранее.
Пятый тест отправляет просроченную cookie. Сервер проверяет срок действия и отклоняет значение. Отдельно проверяется срок бездействия.
Шестой тест меняет роль в payload. Сервер должен отказать при старой подписи. При серверной сессии он должен взять роль из хранилища, а не из cookie.
Седьмой тест повторяет старую cookie после ротации ключа. В период перехода допустима ограниченная поддержка старого ключа. После завершения перехода старая подпись должна перестать работать.
Восьмой тест проверяет отзыв всех сессий. После команды отзыва прежний маркер не должен открывать защищённые страницы.
Добавьте эти проверки в приёмочные критерии. Не ограничивайтесь отчётом «все тесты прошли».
✅ Чек-лист защиты сессии
Начните с инвентаризации. Для каждой cookie запишите назначение, формат, срок жизни и маршруты использования. Отдельно укажите, хранит ли она состояние или только указатель.
- Сервер не доверяет роли, правам и сроку без проверки целостности.
- Cookie содержит случайный идентификатор, если состояние не нужно передавать клиенту.
- Подпись проверяется до разбора и использования полезной нагрузки.
- Неверная подпись приводит к отказу, а не к продолжению работы.
- Сравнение криптографических значений выполняется безопасным способом.
- Ключи не находятся в исходном коде и имеют процесс ротации.
- Старая подпись принимается только в ограниченный период перехода.
- Установлены Secure, HttpOnly и подходящий SameSite.
- Domain и Path ограничены минимально необходимой областью.
- Идентификатор меняется после входа, смены пароля и повышения прав.
- Есть абсолютный тайм-аут и тайм-аут бездействия.
- Есть отзыв одной сессии и всех сессий пользователя.
- WAF фильтрует аномалии, ограничивает частоту и отправляет события в журнал.
- Код приложения сам принимает решение о подлинности и правах.
Проверьте чек-лист вместе с тестами. Каждый пункт должен иметь подтверждение в коде, настройках или журнале.
📌 Что делать при подозрении на подделку
Сначала отзовите подозрительные сессии. Если невозможно определить конкретные значения, отзовите все активные сессии пользователя. Затем выпустите новые идентификаторы.
Проверьте журналы входа и отказов. Ищите смену роли, необычную частоту запросов и повторное использование старых маркеров. Сопоставьте события по времени и устройству.
Если проблема связана с ключом, начните его ротацию. Сократите период приёма старого ключа. После перехода удалите старый ключ из рабочих проверок.
Проверьте не только периметр. Просмотрите все сервисы, которые читают общую cookie. Разные правила в соседних компонентах создают обходной путь.
После исправления повторите негативные тесты. Убедитесь, что старая cookie не открывает доступ. Также проверьте сценарии CSRF и повторного использования.
Зафиксируйте результат в критериях приёмки. Так защита не останется только настройкой на словах.
📚 Читайте также
- Как выявлять фрод по браузерным сигналам: антибот-методики
- Публичное облако и АСУ ТП: как защитить ключи от цепной компрометации
- Токен в URL после Referer: новые каналы утечки и защита веб-приложений
- Зашифрованный диск выключенного ноутбука: границы реальной защиты
- ИИ-письма без ошибок: как выявлять массовые фишинговые кампании
📖 Термины
CSRF · IAM (Identity and Access Management) · WAF · Безопасность приложений · Криптография