Политика одного источника (same-origin policy): скрипт со страницы evil.example не может читать ответы с bank.ru. Источник — схема + домен + порт.
CSRF. Но отправлять запросы на другой источник браузер разрешает: формы, картинки, ссылки. И при отправке он прикладывает cookie этого сайта. Если пользователь вошёл в банк, страница злоумышленника с автоматически отправляемой формой POST bank.ru/transfer выполнит перевод: сервер видит валидную сессионную cookie. Злоумышленник ответа не видит, но ему и не нужно — действие выполнено.
Защита от CSRF:
- SameSite у cookie —
Lax (по умолчанию в современных браузерах): cookie не отправляется в межсайтовых POST и фоновых запросах, только при переходе по ссылке; Strict — никогда в межсайтовых. Основная защита сегодня; - CSRF-токен — случайное значение в форме или заголовке, которое сервер сверяет с сессией. Чужой сайт не знает токена и прочитать его не может (той самой политикой одного источника). Spring Security, Django делают это из коробки;
- **проверка заголовков
Origin/Referer** на изменяющих запросах; - GET не меняет состояние — иначе хватит картинки
<img src=".../delete">; - для API с токеном в заголовке
Authorization CSRF не работает: браузер такой заголовок сам не подставит.
CORS (Cross-Origin Resource Sharing) — механизм, которым сервер разрешает скриптам с других источников читать свои ответы. Браузер добавляет Origin, сервер отвечает Access-Control-Allow-Origin. Для «непростых» запросов (PUT, DELETE, JSON-тело, свои заголовки) браузер сначала шлёт preflight — OPTIONS с вопросом «можно?».
Важно: CORS — это ослабление ограничений, а не защита сервера. Он не останавливает отправку простых запросов (а значит, CSRF), и не мешает curl или серверу злоумышленника — это проверка только в браузере. Опасная конфигурация — отражать любой Origin вместе с Access-Control-Allow-Credentials: true: тогда любой сайт сможет читать данные пользователя.