CSRF-атака — это подделка межсайтового запроса, чтобы выполнить действие на сайте с активной сессией. Представьте: вы вошли в онлайн-банк, закрыли все вкладки и случайно нажали на ссылку мошенника. Сессия личного кабинета ещё активна, значит вредоносный сайт может отправить запрос на перевод денег или смену пароля.
Во время CSRF-атаки браузер выступает подельником: он автоматически прикладывает cookie-файлы к мошенническому запросу. В итоге со стороны онлайн-банка перевод денег или смена пароля выглядят как действия пользователя.
Ваши деньги до сих пор не украли, потому что онлайн-банки используют CSRF-токены, ограничения передачи cookie и повторные подтверждения операций. Далее вы узнаете про условия успешной атаки и 4 способа, как устранить уязвимости CSRF на сайте.
Что такое CSRF
CSRF расшифровывается как Cross-Site Request Forgery, на русском — межсайтовая подделка запроса. Атака опасна тем, что выполняется без доступа к паролю и личным данным. Уязвимость возникает, когда веб-приложение не проверяет, откуда пришёл запрос, и выполняет его только на основании активной сессии пользователя.
Почему CSRF считается критической уязвимостью
До 2017 года CSRF входила в топ-10 критических рисков безопасности в веб-приложениях по версии OWASP. Со временем уровень угрозы снизился, потому что фреймворки начали по умолчанию закрывать эти уязвимости.
Приложения без актуального стека по-прежнему в опасности. Шифрование трафика и сложный пароль не решают проблему, ведь атакующий использует уже установленное доверие между браузером и сервером.
Как работает CSRF-атака
Одновременно должны выполняться 3 условия:
- пользователь авторизован в целевом приложении,
- браузер автоматически отправляет куки с каждым запросом,
- сервер не проверяет источник запроса.
Атака через GET-запрос
Представим, что банковское приложение обрабатывает перевод средств через GET. В таком случае злоумышленник размещает на вредоносной странице тег типа:
<img src="https://bank.com/transfer?to=attacker&amount=1000">
Код автоматически отправляет запрос с куки пользователя, чтобы сервер выполнил перевод на счёт «attacker» в размере «1000».
Атака через POST-запрос
В этом случае злоумышленник размещает на странице скрытую форму и запускает её отправку через JavaScript:
<form action="https://bank.com/transfer" method="POST">
<input type="hidden" name="to" value="attacker">
<input type="hidden" name="amount" value="1000">
</form>
<script>document.forms[0].submit()</script>
4 метода защиты от CSRF
Сервер не отличает сторонние и пользовательские запросы, пока ориентируется только на куки сессии. Защита строится на добавлении данных, которые злоумышленник не знает или не может воспроизвести.
CSRF-токен
Сервер генерирует уникальный токен и привязывает его к сессии пользователя. Токен добавляется в форму скрытым полем и проверяется при каждом запросе. Злоумышленник не знает значение токена, поэтому не может его подделать.
# Генерация токена на сервере и передача в шаблон
csrf_token = generate_csrf_token(session)
# Добавление токена в форму на странице
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
# Проверка токена на сервере при получении запроса
if request.form['csrf_token'] != session['csrf_token']:
abort(403)
SameSite Cookie
Атрибут указывает браузеру не отправлять куки с запросами, которые пришли с других сайтов.
# Браузер отправит куки только с запросами с того же сайта
response.set_cookie('session', 'abc123', samesite='Strict')
# Браузер отправит куки при переходе по ссылке, но не при POST-запросах с других сайтов
response.set_cookie('session', 'abc123', samesite='Lax')
Проверка заголовков Origin и Referer
Сервер проверяет заголовок Origin, который браузер автоматически добавляет к запросам. Если Origin не совпадает с доменом приложения, запрос отклоняется.
Заголовок Referer проверять не стоит: он может быть пустым из-за настроек конфиденциальности или отсутствовать при переходе из закладок, что приводит к ложным блокировкам.
# Получение заголовка Origin из запроса
origin = request.headers.get('Origin')
# Отклонение запроса, если Origin не совпадает с доменом приложения
if origin != 'https://your-app.com':
abort(403)
Double Submit Cookie
Сервер генерирует случайное значение, записывает его в куки и одновременно требует передать это же значение в параметре. При получении запроса сервер сверяет оба значения. Злоумышленник не читает куки чужого домена, поэтому не может передать совпадающее значение.
# Генерация случайного токена
token = generate_random_token()
# Запись токена в куки
response.set_cookie('csrf_token', token)
# Проверка: значение в куки должно совпадать со значением в параметре запроса
if request.cookies.get('csrf_token') != request.form.get('csrf_token'):
abort(403)
CSRF vs XSS: сравнение атак
XSS эксплуатирует доверие пользователя к сайту. Атака заключается во внедрении вредоносного скрипта в страницу приложения. Код выполняется в браузере и получает доступ к кукам, токенам, содержимому форм. Злоумышленник читает данные или выполняет действия от имени пользователя с полным доступом к сессии.
CSRF эксплуатирует доверие сервера к браузеру пользователя и не требует внедрения кода в приложение. Злоумышленник формирует запрос с другого сайта и использует куки сессии, которые браузер отправляет автоматически. Доступа к содержимому страницы или данным сессии у злоумышленника нет.
Полезный блок со скидкой
Если хочется разбираться в таких атаках глубже — не как читатель, а как специалист — у Яндекс Практикума есть курс по кибербезопасности. Закрывает веб-уязвимости, пентест, защиту приложений и сетевую безопасность.
Держите промокод Практикума на любой платный курс: KOD (можно просто на него нажать). Он даст скидку при покупке и позволит сэкономить на обучении.
Как проверить сайт на уязвимость CSRF
Откройте DevTools в браузере, перейдите на вкладку Network и выполните любое действие, которое меняет состояние на сервере (смену email, отправку формы, удаление записи). Найдите соответствующий запрос и проверьте, есть ли в запросе CSRF-токен и проверяет ли сервер заголовок Origin.
Если токена нет, отправьте этот же запрос через curl без токена и проверьте, выполнит ли сервер действие:
# Отправка POST-запроса без CSRF-токена через библиотеку requests
import requests
# Указание куки активной сессии
cookies = {'session': 'your_session_cookie'}
# Отправка запроса без токена
response = requests.post('https://your-app.com/change-email',
data={'email': 'test@test.com'},
cookies=cookies)
# Вывод ответа сервера
print(response.status_code)
Если сервер вернул 200 и выполнил действие, уязвимость подтверждена. Для комплексного аудита используйте специальные инструменты.
Типичные ошибки при защите от CSRF
- Проверка Referer. Заголовок отсутствует в запросах из закладок, при переходе по HTTPS на HTTP и в браузерах с отключённой передачей реферера. Сервер будет блокировать легитимные запросы и пропускать атаки с пустым значением.
- Использование секретного куки вместо токена. Браузер отправляет все куки домена автоматически. Секретное куки не подтверждает намерение пользователя отправить запрос, поэтому не защищает от CSRF.
- Переписывание URL с идентификатором сессии. Добавление session ID в URL скрывает его от злоумышленника, но раскрывает в логах сервера, истории браузера и заголовке Referer при переходе на внешний сайт.
- HTTPS как защита от CSRF. Шифрование не блокирует межсайтовые запросы. Браузер по-прежнему отправляет куки сессии с запросом злоумышленника, даже если соединение зашифровано.
- Многоэтапные транзакции. Разбивка действия на несколько шагов не защищает от CSRF. Злоумышленник воспроизводит каждый шаг последовательно, если способен предсказать или определить параметры каждого шага.
Как ИИ помогает проверять код на CSRF-уязвимости
ChatGPT, Claude и Gemini могут подсказать, в каких местах отсутствие CSRF-защиты критично. Это не замена ручной проверке или сканеру, но ускоряет аудит на этапе разработки.
Скопируйте код обработчика формы и отправьте с таким запросом:
Проверь код на уязвимость CSRF. Укажи, есть ли проверка токена, привязка токена к сессии и проверка заголовка Origin. Если защиты нет, покажи исправленный вариант.
Чат-бот укажет на строки без проверки токена, отсутствие валидации Origin и предложит исправленный вариант.
Нейросеть анализирует только тот код, который вы передали. ChatGPT не знает про конфигурацию сервера и настройки фреймворка, которые могут содержать защиту или уязвимость. Результат нужно сверять с документацией фреймворка и тестировать вручную.
Что дальше
С помощью CSRF злоумышленники не могут читать данные сессии и содержимое страницы. Поэтому бóльшую ценность представляют XSS-уязвимости, когда внедряют вредоносный код на страницу приложения и открывают полный доступ к данным пользователя. Подробнее про кражу cookies и межсайтовую скриптовую атаку читайте здесь.
Советуем дополнительно почитать по теме:
- Пентест: как в ИТ проверяют софт и сети на безопасность — как устроено тестирование на проникновение: методы, команды, инструменты.
- Что такое социальная инженерия и при чём тут кибербезопасность — атаки не через код, а через доверие: фишинг, претекстинг и как от этого защититься.
- Кибербезопасность: какие бывают уязвимости и как от них защититься — обзор угроз, включая уязвимости нулевого дня: где CSRF находится среди других рисков.
Бонус для читателей
Разбираться в уязвимостях и писать защищённый бэкенд — навык, который сложно собрать из разрозненных статей. Если хотите пройти этот путь системно, у Яндекс Практикума есть курс по кибербезопасности — с практикой на реальных уязвимостях, разбором атак и защиты веб-приложений.
Если вы хотите при этом немного сэкономить, напоминаем про наш промокод на курсы Практикума. Он даст скидку при оплате, а вы инвестируете в обучение, чтобы зарабатывать больше!
