Почему CSRF до сих пор опасен для SPA
CSRF‑атака выглядит почти невинно: пользователь авторизован в браузере, злоумышленник подсовывает ему ссылку или невидимую форму, браузер сам добавляет cookies авторизации — и на сервер уходит «легальный» запрос. Для классических сайтов это давно известная проблема, но защита от csrf в веб приложениях с архитектурой SPA усложняется тем, что логика переносится в JavaScript, а API часто живёт на отдельном домене. В результате многие разработчики расслабляются, считая, что «у нас же токены в заголовках, всё ок», и пропускают типичные комбинации CSRF с XSS, уязвимыми CORS‑настройками и слабой валидацией происхождения запросов.
Базовый сценарий CSRF: текстовая диаграмма
Полезно разложить атаку по шагам, в формате простой текстовой диаграммы. Типичный сценарий выглядит так:
Пользователь авторизуется на сайте:
— Браузер ⇨ Сервер: POST /login + пароль
— Сервер ⇨ Браузер: Set-Cookie: session=abc…
Дальше атакующий действует:
— Пользователь открывает вредоносную страницу
— Вредоносный сайт встраивает форму или скрипт:
— Браузер ⇨ Сервер (жертвы): POST /transfer?to=attacker&sum=100
— Браузер автоматически добавляет Cookie: session=abc…
— Сервер видит валидную сессию и выполняет действие
Диаграмма потока:
Пользователь →[логин]→ Сервер →[cookie сессии]→ Браузер
Пользователь →[открыл сайт злоумышленника]→ Вредоносный сайт →[подсовывает форму]→ Браузер →[запрос с cookie]→ Сервер.
CSRF в контексте SPA: где тонко
SPA добавляет свои нюансы. Клиентское приложение на React, Vue или Angular часто общается с backend‑API по REST или GraphQL, использует JWT, session cookies и CORS. На первый взгляд кажется, что JSON‑API «менее уязвим», но браузер по‑прежнему автоматически шлёт cookies к домену API, даже если запрос инициирован чужим сайтом, а это основной триггер CSRF. Кроме того, SPA часто держат состояние авторизации в памяти и localStorage, забывая про ограничения для чувствительных токенов. Безопасность SPA приложений, защита от атак CSRF и их сочетаний с XSS напрямую зависит от того, насколько последовательно вы разделяете доверенные источники запросов и правильно управляете сессионными данными.
Csrf‑токен: что это и как использовать
Если говорить по‑простому, CSRF‑токен — это одноразовый «жетон‑подтверждение намерения», который должен присутствовать в каждом запросе, изменяющем состояние. Отсюда популярный вопрос: csrf токен что это и как использовать его в SPA? Сервер генерирует случайную строку, привязывает её к сессии и ожидает увидеть этот токен в теле или заголовке POST/PUT/DELETE‑запросов. В отличие от cookie, которые браузер прикрепляет автоматически, токен должен быть добавлен именно JavaScript‑кодом вашего приложения. Если атакующий просто заставит браузер отправить форму с чужого сайта, у него не будет корректного токена, и сервер отклонит запрос, тем самым разрывая типичный CSRF‑сценарий.
Текстовая схема проверки CSRF‑токена

С точки зрения сервера всё выглядит примерно так:
1) Авторизация:
Браузер → Сервер: POST /login
Сервер:
— создаёт сессию: session=abc
— генерирует csrf=xyz
— отвечает: Set-Cookie: session=abc; HttpOnly
+ JSON: {«csrfToken»: «xyz»}
2) Запрос изменения:
SPA → Сервер: POST /profile/update
Заголовки:
— Cookie: session=abc
— X-CSRF-Token: xyz
Сервер проверяет: есть ли сессия, совпадает ли токен в заголовке с сохранённым значением. Если нет — 403 Forbidden. Эта логика одинаково важна и для REST, и для GraphQL‑эндпоинтов, даже если они используются только фронтендом SPA.
CSRF и session cookies против JWT: сравнение подходов
Многие надеются, что переход на JWT «сам по себе» решает CSRF, потому что токен не лежит в cookie. Но если JWT хранится в cookie (что делают ради безопасности от XSS) и автоматически отправляется браузером, CSRF возвращается. Если же JWT хранится в localStorage и передаётся через Authorization‑заголовок, атака через простой

