Так ли безопасно бездумно переходить по ссылкам, которые присылают нам знакомые (и не очень) люди в интернете? Если вы ответили «да», то спешу вас разочаровать — это не совсем так. Бывают случаи, когда при переходе по ссылке браузер фоном выполняет какое-то не запрошенное лично вами действие от вашего имени на другом сайте. Это и есть CSRF — Cross Site Request Forgery.
И хорошо, если это что-то безобидное вроде лайка на посте или подписки на рассылку. Но сам факт, что злоумышленник может тайно действовать от вашего лица, должен пробудить некоторую бдительность.
В статье про CORS мы вскользь упомянули этот вид атаки, а сегодня разберемся с ней в деталях
- Как выглядит CSRF атака. Разбираем пошагово
- Почему не спасает Same‑Origin Policy?
- Как совершаются CSRF-атаки
- 1. GET-запросы с побочными эффектами
- 2. Подделка POST-запроса через скрытую форму
- Как защититься? Три основных метода
- Метод 1: CSRF-токены
- Метод 2: SameSite cookies
- Метод 3: Проверка Referer / Origin
- А разве CORS недостаточно?
- Итог: что важно запомнить
Как выглядит CSRF атака. Разбираем пошагово
Допустим, мы зашли в уже знакомый нам по статье про CORS вымышленный интернет-банк bank.ru и авторизовались с целью оплатить коммунальные платежи. В этот момент в браузере поселилась сессионная кука — пропуск, который подтверждает: «Это владелец счета, он может совершать с ним и его содержимым любые действия».
Спустя 5 минут работы с платёжками, мы устали от этой неприятной обязанности и открыли новую вкладку, чтобы отвлечься и расслабиться. По присланной в почту ссылке мы перешли на сайт kotyki.ru. Но администратор kotyki.ru хочет получить доступ к нашим деньгам. Зная о «дырявости» API нашего банка, он добавил на страницу такой код:
<img src="https://bank.ru/transfer?to=hacker&amount=1000" width="1" height="1">
Картинка размером в 1 пиксель, совсем невидимая для человека. Но браузер, увидев тег <img>, автоматически пытается загрузить содержимое атрибута src. Он отправляет GET-запрос на bank.ru/transfer?to=hacker&amount=1000. При этом автоматически к запросу прикрепляются ваши сессионные куки (потому что они принадлежат тому домену, на который вы отправляете запрос).
Сервер банка получает запрос. Видит: «О, куки. Они от настоящего владельца счета. Значит, сам владелец хочет перевести 1000 рублей хакеру. Кто я такой, чтобы ему мешать». И выполняет перевод.
Почему не спасает Same‑Origin Policy?
Вы можете спросить: «Но как же политика одинакового источника? Она же должна блокировать запросы с чужого сайта!»
Вот тут главная загвоздка. Same‑Origin Policy (SOP) блокирует чтение ответа, но не отправку запроса!
- Браузер позволяет отправить POST/GET‑запрос с
kotyki.ruнаbank.ru. - Браузер прикрепляет куки от
bank.ru, если они у него есть. - Браузер не показывает ответ от
bank.ruсайтуkotyki.ru(только тут и срабатывает SOP).
Но тому что совершает CSRF-атаку и не нужно читать ответ. Ему достаточно, чтобы запрос исполнился. Перевелись деньги — хорошо. Пользователь сменил пароль — отлично. Сообщение опубликовано — пусть и маленькая, но победа.
Как совершаются CSRF-атаки
Существует две линии атаки
1. GET-запросы с побочными эффектами
Мы разобрали именно такой пример. Наш условный bank.ru делает перевод через GET — GET /transfer?amount=1000&to=hacker. Например, любой тег <img> или <a href="..."> может дернуть эту ссылку. Хакеру достаточно заманить вас на страницу с такой ловушкой.
Как решается. При дизайне API никогда не используйте GET для изменений. Только POST, PUT, DELETE. Но и этого мало, потому что POST тоже можно подделать.
2. Подделка POST-запроса через скрытую форму
Злоумышленник создает на своем сайте невидимую форму и отправляет её автоматически через JavaScript
<form id="hack" action="https://bank.ru/transfer" method="POST">
<input name="to" value="hacker">
<input name="amount" value="1000">
</form>
<script>document.getElementById('hack').submit();</script>
Когда вы заходите на сайт, скрипт мгновенно отправляет форму и снова без вашего участия. Браузер снова послушно прикрепляет куки. Сервер думает, что это вы.
Как решается: Использовать CSRF-токены, о которых знает только ваш сайт (и которые не может угадать злоумышленник).
Как защититься? Три основных метода
Метод 1: CSRF-токены
Сервер при каждой генерации страницы добавляет в форму секретный случайный токен.
<form method="POST" action="/transfer">
<input type="hidden" name="csrf_token" value="a7f3d9e2...">
<input name="amount">
<button>Перевести</button>
</form>
При отправке формы сервер проверяет в том числе и совпадение токенов. У зловредного админа kotiky.ru нет возможности узнать этот токен. Поэтому его подделанная форма не пройдет.
Плюсы: Надежно.
Минусы: Требует хранить состояние на сервере.
Метод 2: SameSite cookies
Браузеры теперь поддерживают атрибут кук SameSite. Вы говорите браузеру:
Set-Cookie: session_id=abc123; SameSite=Strict
SameSite=Lax— предъяви куки если я действительно перехожу по ссылке или инициирую действие (но не при отправке формы из iframe или скрипта)SameSite=Strict— если запрос приходит с другого сайта не предъявляй куки вообще никогда
То есть если банк поставит SameSite=Strict, то при заходе на kotyki.ru браузер не прикрепит куки банка к поддельному запросу. Атака не сработает.
Плюсы: Не требует изменять код форм.
Минусы: Старые браузеры не поддерживают (но сейчас уже все современные поддерживают).
Метод 3: Проверка Referer / Origin
Сервер смотрит на заголовок Referer или Origin. Откуда пришел запрос? Если Referer — kotyki.ru, а должен быть только bank.ru — сервер отклоняет запрос.
if (req.headers.origin !== 'https://very-bank.com') {
return res.status(403).send('CSRF атака!');
}
Плюсы: Просто.
Минусы: Referer может отсутствовать (например, при переходе из HTTPS в HTTP или из-за политик конфиденциальности).
А разве CORS недостаточно?
Легко перепутать CORS и CSRF. Четыре буквы, начинаются с C — очень похоже. Но различать их стоит и запомнить различия можно так:
- CORS защищает данные. Он не дает чужому сайту прочитать ответ от вашего сервера.
- CSRF защищает действия. Он не дает чужому сайту выполнить опасный запрос от вашего имени.
У нас может быть строжайше настроенный CORS (Access-Control-Allow-Origin: только мой сайт), и все равно наши пользователи будут уязвимы для CSRF, потому что запрос приходит с их IP, с их куками, а сервер не знает, что его инициировал злой админ kotiky.ru.
Итог: что важно запомнить
- CSRF — атака, при которой вредоносный сайт заставляет браузер выполнить запрос на сайт, где вы залогинены.
- Уязвимость существует, потому что браузер автоматически прикрепляет куки к запросам на любой сайт, даже если запрос пришел с чужой страницы.
- Спасают CSRF-токены (спрятанные в формах) или SameSite cookies (атрибут кук).
- CORS не защищает от CSRF. CORS — про чтение ответа. CSRF — про выполнение действия.
- Любое действие, которое что-то меняет (деньги, пароль, подписка, удаление), должно требовать либо CSRF-токен, либо повторную аутентификацию (пароль/капчу).
Чтобы проверить, уязвимы ли вы, можно попробовать отправить форму из HTML-файла на локальной машине. Если сработало — бросайте читать непонятные блоги и бегите чинить защиту.
Желаю успехов!







