CSRF – это аббревиатура, обозначающая особый тип атаки на сайты, которая предполагает перехват данных для авторизации и использование их в целях злоумышленников. При этом жертва сама никаких действий совершать не будет, а целевой сайт будет думать, что работает с реальным клиентом, который ранее авторизовался в аккаунте (данные об этом сохраняются в браузере, а в частности, в куках).
Соответственно, ошибка CSRF возникает далеко не на пустом месте. Обычно это связано с заполнением форм или с отправкой прямых HTTP-запросов к серверу, которые могут что-то изменять в базах данных. Если вы написали свой скрипт парсинга и столкнулись с ошибкой CSRF-токена, то этот материал для вас.
Ниже расскажем что такое CSRF-ошибка, почему и в каких случаях она возникает, как её избежать при создании парсеров.
Основы токенов CSRF: как работает защита от межсайтовой подделки запросов
Механизм атаки известен с давних пор, ещё с 1990-х годов. Если сильно упростить, то CSRF (расшифровывается как Cross-Site Request Forgery, наиболее точный перевод на русский «межсайтовая подделка запроса») сводится к тому, что злоумышленник использует уже имеющиеся данные авторизации на целевом сайте, хранящиеся в браузере, для отправки своих запросов.
Момент перехвата обычно происходит при посещении стороннего сайта, который контролируется злоумышленником – на нём исполняется специальный скрипт, который задействует куки браузера и подделывает (перенаправляет) запрос.
Защита от такого типа атак тоже давно отработана – это использование специальных CSRF-токенов, которые привязываются к пользовательским сессиям (сессии хранятся на сервере, а не в браузере) или даже генерируются для каждого нового запроса/действия пользователя. Если валидный токен не участвует в обращении к серверу (методы POST, PUT, DELETE, заполнение имеющихся форм на страницах), то они будут возвращать CSRF-ошибку.
Ещё один механизм защиты – анализ поля HTTP_REFERER, для понимания, что запрос отправляется посредником, а не напрямую.
Что такое ошибка CSRF и почему она возникает при парсинге
В общем-то уже здесь становится понятно, как парсер сталкивается с CSRF-ошибками. Ситуация может возникать в следующих случаях:
- если скрипт пытается авторизоваться на сайте (войти в личный кабинет пользователя) или совершить действия под уже авторизованным аккаунтом;
- если обращение к сайту происходит в виде прямых POST-запросов (как вариант, вместо POST могут использоваться PUT или DELETE, но это маловероятно при парсинге), например, при настройке параметров фильтрации каталога/корзины;
- если используется API-эндпоинты, которые защищены web-middleware (в случаях, когда вы пытаетесь обращаться к внутреннему API сайта/веб-сервиса);
- если при взаимодействии с сайтом не передаётся заголовок Referer/Origin, который ожидает увидеть целевой сайт;
- если парсер не работает с куками (а они, например, могут обязательно требоваться для взаимодействия);
- если парсер пытается заполнить и отправить форму, которая защищена CSRF-токеном.
Такие ситуации возникают относительно редко, но разработчик должен быть готов к ним.
Как обнаружить и отладить ошибки CSRF в вашем парсере

Основная проблема ошибок, связанных с межсайтовой подделкой запросов в том, что целевые сайты могут не возвращать описания ошибок, которые бы прямо указывали на отсутствие CSRF-токена. Реализовывать поддержку кук и CSRF для всех сайтов без разбора тоже неразумно – в 90% случаев она будет излишней.
Итак, как следует выявлять CSRF-ошибки.
Явное указание на CSRF в описании страницы с ошибкой
Отдельной ошибки в HTTP-кодах для CSRF не существует. Поэтому сервер в норме использует стандартные механизмы ответов: 403 (запрет доступа), 419 (отдаётся при истечении сессий, в том числе, при отсутствии CSRF-токена), 400 (плохой/неправильный запрос).
Реже прямое указание можно найти непосредственно в тексте ошибки. Логично искать ключевые слова «token», «CSRF», «invalid».
В некоторых случаях сервер может возвращать пустые ответы, как бы игнорируя запросы парсера.
Ни один из этих вариантов не может дать 100% гарантий, что вы столкнулись с баном из-за отсутствия CSRF-токена. Исключение – прямое указание на CSRF в тексте ошибки (если вы его получили, считайте, что вам сильно повезло).
Подробнее о том, как могут проявлять себя продвинутые системы защиты (WAF).
Диагностика поведения страницы с помощью сравнения с «эталоном»

В качестве «эталонного образца» будет выступать поведение и ответы сервера при работе через классический браузер. То есть вам нужно обратиться к целевой странице вручную – из своего реального браузера, со всеми его настройками, куками и другими атрибутами цифрового отпечатка.
Когда вы убедились, что сайт работает «как надо», можно обращаться к странице или к API-интерфейсу из своего парсера и сравнивать результирующий ответ.
Если сервер возвращает ошибку или вообще ничего не возвращает, то с большой вероятностью вы не смогли правильно сымитировать работу реального клиента (в том числе, возможен отказ из-за отсутствия CSRF-токена).
Теперь вам нужно обратно вернуться в реальный браузер и детально изучить процесс обмена данными с сервером. Чаще всего нужно погрузиться в анализ POST-запросов (инструменты разработчика/DevTools → вкладка Сеть/Network).
На что конкретно обращать внимание:
- есть ли cookie и какие именно (изучите их содержимое – sessionid, PHPSESSID, laravel_session; чтобы узнать куки сайта перейдите в DevTools → Application → Storage → Cookies → выберите домен → тут полный список кук отдельного сайта; токен может быть записан непосредственно в куках – csrftoken, _csrf, XSRF-TOKEN и т.п.);
- есть ли X-CSRF-Token в заголовке (данные могут передаваться в виде специального HTTP-заголовка, например, X-CSRF-Token или X-XSRF-TOKEN), скрытый input (в этом случае токен «вшит» на уровне HTML-кода, например: <input type="hidden" name="csrf_token" value="XXXXXX">) или специальный мета-тег (<meta name="csrf-token" content="XXXXXX">);
- Referer / Origin (чтобы сервер пропустил запрос, важно указать правильный источник перехода; в качестве реферера могут выступать отдельные внутренние URL-адреса, например, адрес формы «Referer: https://example.com/form»; для изучения HTTP-заголовков используйте этот инструмент: DevTools → Network → конкретный POST → вкладка Headers);
- Content-Type (современные веб-приложения могут общаться внутри/между разными компонентами по API-интерфейсу в определённом формате – XML, JSON и т.п., в данных могут скрываться CSRF-токены, искать соответствующие данные следует здесь: DevTools → Network → Headers → Request Headers → Content-Type);
- порядок запросов (если алгоритм обращения к серверу будет отличаться от стандартного, то соединения могут помечаться как сомнительные и блокироваться; нормальное поведение для браузера или для web-клиента выглядит так: сначала происходит обращение к странице через GET-запрос, в ответе клиенту передаются разные параметры, в том числе актуальный CSRF-токен, теперь клиент может обратиться к серверу с POST-запросом; прямые POST-запросы автоматически выпадают из такой схемы).
Основные стратегии предотвращения ошибок CSRF при парсинге

Чтобы исключить или минимизировать риски возникновения ошибок скрапинга с CSRF-токенами, мы предлагаем следующие варианты.
1. Удержание сессий при парсинге
Этот подход подразумевает следующие принципы:
- CSRF почти всегда привязан к сессионным куках, поэтому важно сохранять все данные сессии для одной цепочки запросов, адресованных к одному целевому сайту. Это особенно касается тех проектов, с которыми вы работаете через авторизацию в аккаунте/личном кабинете.
- При наличии защиты с токенами CSRF прокси желательно тоже удерживать – принудительная ротация выходных IP-адресов должна быть минимальной (только в случае крайней необходимости).
- POST-запросы нельзя отправлять «вхолостую» – только с сохранением кук.
Иными словами, если у вас есть POST-запросы, то парсер не может обращаться к страницам напрямую, без предварительного открытия сессии и получения cookie.
Не упустите из виду: токены, как и сессии, могут иметь свой срок жизни. Установить актуальный период действия можно только опытным путём. Самый распространённый диапазон значений – около 10-30 минут, но это сложно назвать «золотым стандартом». У каждого сайта может быть своя настройка времени действия – вплоть до привязки CSRF-токена к каждому новому запросу (активности пользователя).
2. Написание специального модуля, который будет выявлять CSRF-токены и логировать действия парсера для анализа
Вы должны предусмотреть все типовые ситуации передачи CSRF-токена (в мета-тегах, в JSON/XML, в HTTP-заголовках, в формах, в виде JavaScript-переменных и т.п.) и уметь отдавать полученный токен в том формате, в котором его ожидают от браузера.
Если вы хотите быстрее обнаруживать CSRF-ошибки и недочёты при работе парсера с токенами, то желательно написать код, который бы логировал все действия парсера и ответов сервера. Так вам будет проще выявлять шаги, на которых вы получаете проблему.
3. Задействовать headless-браузеры
В особо сложных случаях вы можете получать ошибки и баны не из-за CSRF-токенов, а из-за срабатывания систем защиты сайта. Ошибки, которые будет возвращать сервер, малоинформативные, а что ещё хуже – одинаковые в обоих случаях.
Поэтому, вместо того чтобы тратить время и силы на имитирование всего, что только можно, достаточно запустить парсинг через реальный браузер. Проблема с куками, а также с большинством других систем защиты отпадёт сама собой. Если и это не поможет, то нужно будет углубиться в имитирование поведения пользователя, а также позаботиться о скрытии следов работы headless-браузера.
Обратите внимание, при работе через headless и антидетект-браузеры, прокси должны ротироваться по-другому – на уровне экземпляра браузера, а не самого парсера.
Лучшие веб-драйвера: Playwright, Selenium, Puppeteer, Nodriver.
Парсинг с защитой от CSRF-атак: реальный рабочий процесс

Пример простейшего парсера, который ищет CSRF-токен в мета-заголовке и включает его при работе на уровне сессии (за синтаксический анализ отвечает BeautifulSoup):
import requests
from bs4 import BeautifulSoup
BASE_URL = "https://example.com"
FORM_URL = f"{BASE_URL}/profile"
SUBMIT_URL = f"{BASE_URL}/api/update"
session = requests.Session()
# 1. Загружаем страницу, чтобы получить токен и cookies
response = session.get(FORM_URL)
response.raise_for_status()
# 2. Извлекаем CSRF-токен из <meta name="csrf-token">
soup = BeautifulSoup(response.text, "html.parser")
csrf_token = soup.find("meta", {"name": "csrf-token"})["content"]
print("CSRF-токен:", csrf_token)
print("Cookie after GET:", session.cookies.get_dict())
# 3. Отправляем POST с этим токеном в заголовке
headers = {
"X-CSRF-Token": csrf_token,
"Referer": FORM_URL, # часто обязателен
"Origin": BASE_URL, # иногда требуется
"User-Agent": "Mozilla/5.0", # имитация браузера
}
payload = {"name": "John", "city": "Berlin"}
post_response = session.post(
SUBMIT_URL,
json=payload,
headers=headers
)
print("POST status:", post_response.status_code)
print("Server response:", post_response.text[:300])
Заключение с чеклистом

Вот контрольный список для безопасного парсинга без CSRF-атак:
- Не стоит парсить зоны сайтов с авторизацией. А если делаете это, то автоматически предусматривайте механизм работы с CSRF-токенами (они могут быть где угодно).
- Не стоит использовать POST-запросы без явной необходимости, а также PUT, PATCH и DELETE. Самый безопасный путь – GET-запросы.
- Если-таки без POST-запросов не обойтись, то парсер должен перейти на модель работы на основе сессий и кук (именно на них и завязано 90% всех алгоритмов безопасности, в том числе CSRF-токены).
- Если вы нашли место, в котором сервер передаёт CSRF-токен, нужно реализовать механизм его проброса правильным способом – тем, который ждёт от вас целевой сайт. Любое отклонение, даже в последовательности шагов, может привести к срабатыванию систем защиты.
- Научите свой парсер обрабатывать ошибки. Как минимум у вас должен быть механизм детального логирования ответов, чтобы вы могли провести быстрый анализ проблем.
- Если зашли в тупик, используйте сравнение с эталоном. Ручная отладка с помощью инструментов разработчика в реальном браузере поможет ответить на все ваши вопросы.
Ещё по теме: Лучшие практики для веб-скрапинга без блокировок.

