Перейти к содержанию
Innovialabs

Безопасные cookie: флаги HttpOnly, Secure и SameSite

Практика3 мин

Коротко

Cookie сессии должна быть HttpOnly, Secure, с SameSite=Lax (или Strict), с префиксом __Host- и ограниченным сроком жизни на сервере. Эти флаги закрывают кражу через XSS, утечку по HTTP и большую часть CSRF.

Короткий ответ

Для cookie сессии в большинстве веб-приложений подходит такой набор:

Set-Cookie: __Host-session=abc123; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=86400

Каждый атрибут закрывает свою угрозу, и убрать любой из них — значит открыть конкретную дыру.

Что делает каждый флаг

ФлагЧто делаетОт чего защищает
HttpOnlycookie недоступна из JavaScript (document.cookie)кража сессии через XSS
Securecookie отправляется только по HTTPSперехват в открытой сети, при случайном переходе на HTTP
SameSiteограничивает отправку при межсайтовых запросахCSRF, часть утечек между сайтами
Domainрасширяет cookie на поддомены— (наоборот, расширяет поверхность атаки)
Pathограничивает путьне является мерой безопасности
Max-Age / Expiresсрок жизни в браузередолгоживущие украденные сессии

HttpOnly не спасает от XSS как такового: вредоносный скрипт всё ещё может слать запросы от имени пользователя. Но он не сможет унести cookie и использовать её с другого устройства.

Domain лучше не указывать вовсе. Без него cookie становится host-only и уходит только на точный домен, который её выставил.

Lax, Strict и None

  • Strict — cookie не уходит ни с одним межсайтовым запросом, даже при клике по ссылке из почты или мессенджера. Пользователь, пришедший по внешней ссылке, на первой странице выглядит разлогиненным.
  • Lax — cookie отправляется при обычном переходе по ссылке (GET верхнего уровня), но не с межсайтовыми POST, fetch, iframe и картинками. Разумный выбор по умолчанию.
  • None — cookie уходит всегда. Нужна для виджетов в чужих iframe и некоторых кросс-доменных сценариев. Обязательно вместе с Secure, иначе браузер её отвергнет.

Если SameSite не указан, разные браузеры ведут себя по-разному: часть применяет Lax по умолчанию, часть — нет. Всегда задавайте значение явно.

Частый компромисс: две cookie — одна Strict для опасных действий (смена пароля, платежи), другая Lax для обычной навигации.

Браузер проверяет префиксы в имени и не примет cookie, которая не соответствует правилам:

  • __Secure- — cookie обязана иметь Secure и быть выставлена с HTTPS-страницы.
  • __Host- — то же плюс Path=/ и запрет на Domain. Такую cookie нельзя подменить с поддомена.

Для сессии __Host- — лучший вариант, если не нужен общий вход на поддоменах.

Время жизни сессии

  • Cookie без Max-Age и Expires — сессионная, но браузеры с восстановлением вкладок могут хранить её днями. Не рассчитывайте на «закрыл браузер — вышел».
  • Главный срок жизни задаётся на сервере: idle-таймаут (сессия умирает при бездействии) и абсолютный таймаут (даже активная сессия заканчивается).
  • При входе и повышении прав выдавайте новый идентификатор сессии — это закрывает session fixation.
  • При выходе удаляйте сессию на сервере, а не только cookie в браузере.
  • Для админок и финансовых разделов сроки короче, чем для обычного кабинета.

Ошибки, которые ломают вход или сливают сессии

  • Secure на локальном HTTP-окружении с нестандартным хостом — cookie не сохраняется, вход «молча» не работает. Используйте HTTPS локально или localhost.
  • SameSite=None без Secure — cookie отбрасывается браузером.
  • SameSite=Strict при OAuth или платёжном редиректе: провайдер возвращает пользователя межсайтовым запросом, и состояние сессии теряется. Особенно часто ломаются возвраты через POST — они не получают cookie даже при Lax.
  • Domain=.example.com без необходимости — cookie видят все поддомены, включая тестовые и сторонние сервисы.
  • Токен сессии в localStorage — он доступен любому скрипту на странице.
  • Одинаковое имя cookie на разных поддоменах с разным Path — браузер отправляет несколько значений, сервер берёт не то.
  • Бесконечные «запомнить меня» без отзыва токенов при смене пароля.

Справка по атрибутам — в документации MDN Set-Cookie.

Частые вопросы

Да, и это часто безопаснее localStorage: с HttpOnly скрипты его не прочитают. Но тогда нужна защита от CSRF — SameSite плюс токен или проверка Origin.

Поможем с этим

Если хотите не разбираться самим, а получить результат — этим мы занимаемся каждый день.

Обсудить проект

Читайте также

Есть идея или задача? Расскажите нам

Ответим в течение рабочего дня и предложим решение. Консультация бесплатная.