В этой статье разберём конкретно, как работают механизмы защиты от межсайтовых подделок запросов и почему сочетание CSRF токенов и атрибута SameSite в куках даёт надёжную линию обороны. Я постараюсь объяснить не только теорию, но и практические приёмы, которые можно применить прямо сегодня в проекте.

Почему CSRF представляет реальную угрозу

CSRF — атака, при которой злоумышленник вынуждает браузер пользователя выполнить нежелательное действие от имени этого пользователя, например перевести деньги или сменить пароль. Успех обычно зависит от того, что браузер автоматически отправляет куки с идентификатором сессии при запросах к сайту.

Классический пример — форма на злоумышленном сайте, которая отправляет POST на целевой ресурс с сессионной кукой пользователя. Пользователь даже не подозревает о происходящем, если на целевом сайте нет дополнительной проверки подлинности запроса.

Механика CSRF токенов

CSRF токен — случайная последовательность, генерируемая сервером и встраиваемая в формы или заголовки запросов. При получении запроса сервер сверяет токен с ожидаемым значением и отклоняет запрос, если совпадения нет.

Существует два распространённых подхода: синхронизированный токен (synchronizer token) и схема «double submit cookie». В первом случае токен хранится в сессии на сервере и встраивается в форму при рендеринге; при запросе сервер сверяет отправленный токен с тем, что в сессии. Во втором подходе токен дублируют в куку и в теле запроса, проверяя совпадение без обращения к сессии.

Где лучше применять CSRF токены

Токены подходят для классических веб-форм и любых state-changing запросов, которые инициируются браузером пользователя. Они особенно полезны, когда приложение использует куки для аутентификации. В API, где используется OAuth или токены в заголовках Authorization, CSRF токены часто не нужны.

Важно генерировать достаточно длинные случайные значения и менять их при повторной авторизации или подозрении на компрометацию. Также следите за тем, чтобы токены были привязаны либо к сессии, либо к конкретному пользователю, иначе защита теряет смысл.

SameSite: поведение куки при кросс-сайтовых запросах

Атрибут SameSite у куки указывает браузеру, отправлять ли эту куку при кросс-сайтовых переходах. Он помогает уменьшить вероятность успешного CSRF, блокируя автоматическую отправку куки из стороннего контекста.

Значения атрибута коротко сводятся к трем вариантам и имеют важные нюансы в браузерах. Ниже таблица с их отличиями.

Значение Отправляется при навигации по ссылке Отправляется при POST из стороннего сайта Требования
Strict Только при прямом доступе к сайту Не отправляется Максимальная изоляция
Lax Отправляется при безопасной навигации (GET) Чаще не отправляется Баланс удобства и безопасности
None Отправляется всегда Отправляется всегда Требует Secure для HTTPS

Практические последствия настроек SameSite

SameSite=Strict даёт наивысшую защиту, но может нарушить некоторые сценарии — например, если пользователь переходит по почтовой ссылке и ожидает сохранённой сессии. Lax стал разумным значением по умолчанию в большинстве случаев, так как не ломает переходы по ссылкам, но всё равно ограничивает автоматическую отправку при небезопасных методах.

Важно помнить, что для браузеров запись SameSite=None должна сопровождаться флагом Secure, иначе кука может быть проигнорирована. Также некоторые устаревшие браузеры по-разному трактуют атрибут, поэтому тестирование остаётся обязательным.

Сочетание CSRF токенов и SameSite в реальной архитектуре

Комбинация токенов и SameSite предоставляет defence-in-depth: если одна защита обходит, есть запасной механизм. Например, при Lax большинство кросс-сайт POST-запросов не будет содержать куки, но в редком случае, если кука всё же попала, проверка CSRF токена остановит запрос.

Такая комбинация особенно полезна для сайтов, которые активно используют формы и сохраняют сессию в куках. Я рекомендую никогда не полагаться на один только механизм, потому что требования к совместимости и специфические сценарии поведения браузеров могут дать неожиданные дыры.

Когда одной атрибуты куки достаточно

Если ваше приложение — одностраничное приложение с API, где все запросы аутентифицируются через заголовок Authorization и куки не используются для сессий, SameSite не решает ничего, и CSRF токены тоже не нужны. В противоположном случае полагаться только на SameSite рискованно.

Кроме того, при использовании внешних виджетов, iframe или сторонних интеграций требуются тонкие настройки; иногда придётся выбрать SameSite=None для корректной работы, и тогда токены возвращаются в игру в качестве основной защиты.

Проблемы и подводные камни

Сильная иллюзия безопасности возникает, когда разработчики ставят SameSite=None и забывают про CSRF токены, считая, что проблема решена. Я сталкивался с такими кейсами: интеграция виджета сломалась в некоторых мобильных браузерах из-за блокировки сторонних куков, и команда оказалась без запасной защиты.

Другой момент: fetch и XMLHttpRequest по умолчанию не отправляют куки в кросс-доменных запросах без credentials: ‘include’ или с соответствующей опцией. Это влияет на поведение API и предъявляет дополнительные требования к конфигурации CORS и проверкам на сервере.

Особенности при использовании iframe и сторонних сервисов

Если ваше приложение встраивают в iframe на сторонних доменах, SameSite может блокировать куки — это полезно для защиты, но иногда ломает функциональность. В таких случаях приходится выбирать между удобством и безопасностью и обеспечивать защиту CSRF другими методами.

Также современные браузеры иногда применяют дополнительные политики по блокировке трекеров и third-party cookies, что надо учитывать при проектировании авторизации и интеграций.

Практическая инструкция: как внедрить защиту правильно

Ниже — сжатая последовательность действий, которая поможет настроить защиту без лишних сложностей. Реализация займет немного времени, но значительно уменьшит риск успешной атаки.

  • Убедитесь, что все state-changing endpoints требуют POST/PUT/DELETE и проверяют валидность CSRF токена.
  • Генерируйте криптографически стойкие токены и привязывайте их к сессии или пользователю.
  • Установите для куки сессии SameSite=Lax по умолчанию; при необходимости для интеграций используйте None вместе с Secure.
  • Если используются AJAX-запросы, отправляйте токен в заголовке (например, X-CSRF-Token) и проверяйте его на сервере.
  • Проводите тестирование на разных браузерах и устройствах, включая мобильные, чтобы не сломать легитимные сценарии.
  • Логируйте неудачные проверки CSRF, это поможет обнаруживать подозрительную активность.

Дополнительные рекомендации по реализации

Для SPA удобнее хранить токен в куке с HttpOnly=false и извлекать его на клиенте, либо сохранять в памяти при загрузке страницы. Важно избежать хранения токена в localStorage, если присутствует риск XSS, так как тогда токен можно украсть.

Периодическая ротация токенов усложняет жизнь атакующим и снижает вероятность использования украденного токена. Но будьте осторожны с частой ротацией в долгих пользовательских сессиях — это может вызывать ложные срабатывания.

Личный опыт и типичные ошибки команд

В одном из проектов мы столкнулись с проблемой: виджет платёжной формы встраивался на сторонних сайтах и требовал SameSite=None для корректной работы. Переход на None был неизбежен, поэтому мы усилили проверку на стороне сервера, внедрив строгие CSRF токены и привязку к origin. Это позволило сохранить функциональность и не ухудшить безопасность.

Частая ошибка — отключать CSRF проверки «пока тестируем». Это приводит к тому, что забытые отключения попадают в продакшн. У нас сработало правило: любые изменения в механизмах безопасности должны сопровождаться автоматизированными тестами и обязательным код-ревью.

Короткий план действий для команды

Если вы внедряете защиту в существующий проект, начните с анализа точек входа, где выполняются state-changing операции. Затем примените настройки куки и добавьте CSRF токены в формы и AJAX-запросы. Проведите регрессионные тесты на ключевых платформах.

Внедрите мониторинг неудачных попыток валидации и держите документацию для интеграторов, чтобы они знали, какие заголовки или куки требуются для корректной работы.

Синтезируя всё изложенное, можно сказать так: SameSite уменьшает поверхность атаки, но не заменяет валидную проверку запроса на стороне сервера. Комбинация этих инструментов, корректная конфигурация куки и внимательное тестирование — реальный путь к надёжной защите форм и критичных операций в веб-приложении.