CSRF – это аббревиатура, обозначающая особый тип атаки на сайты, которая предполагает перехват данных для авторизации и использование их в целях злоумышленников. При этом жертва сама никаких действий совершать не будет, а целевой сайт будет думать, что работает с реальным клиентом, который ранее авторизовался в аккаунте (данные об этом сохраняются в браузере, а в частности, в куках).
Соответственно, ошибка CSRF возникает далеко не на пустом месте. Обычно это связано с заполнением форм или с отправкой прямых HTTP-запросов к серверу, которые могут что-то изменять в базах данных. Если вы написали свой скрипт парсинга и столкнулись с ошибкой CSRF-токена, то этот материал для вас.
Ниже расскажем что такое CSRF-ошибка, почему и в каких случаях она возникает, как её избежать при создании парсеров.
Механизм атаки известен с давних пор, ещё с 1990-х годов. Если сильно упростить, то CSRF (расшифровывается как Cross-Site Request Forgery, наиболее точный перевод на русский «межсайтовая подделка запроса») сводится к тому, что злоумышленник использует уже имеющиеся данные авторизации на целевом сайте, хранящиеся в браузере, для отправки своих запросов.
Момент перехвата обычно происходит при посещении стороннего сайта, который контролируется злоумышленником – на нём исполняется специальный скрипт, который задействует куки браузера и подделывает (перенаправляет) запрос.
Защита от такого типа атак тоже давно отработана – это использование специальных CSRF-токенов, которые привязываются к пользовательским сессиям (сессии хранятся на сервере, а не в браузере) или даже генерируются для каждого нового запроса/действия пользователя. Если валидный токен не участвует в обращении к серверу (методы POST, PUT, DELETE, заполнение имеющихся форм на страницах), то они будут возвращать CSRF-ошибку.
Ещё один механизм защиты – анализ поля HTTP_REFERER, для понимания, что запрос отправляется посредником, а не напрямую.
В общем-то уже здесь становится понятно, как парсер сталкивается с CSRF-ошибками. Ситуация может возникать в следующих случаях:
Такие ситуации возникают относительно редко, но разработчик должен быть готов к ним.
Основная проблема ошибок, связанных с межсайтовой подделкой запросов в том, что целевые сайты могут не возвращать описания ошибок, которые бы прямо указывали на отсутствие CSRF-токена. Реализовывать поддержку кук и CSRF для всех сайтов без разбора тоже неразумно – в 90% случаев она будет излишней.
Итак, как следует выявлять CSRF-ошибки.
Отдельной ошибки в HTTP-кодах для CSRF не существует. Поэтому сервер в норме использует стандартные механизмы ответов: 403 (запрет доступа), 419 (отдаётся при истечении сессий, в том числе, при отсутствии CSRF-токена), 400 (плохой/неправильный запрос).
Реже прямое указание можно найти непосредственно в тексте ошибки. Логично искать ключевые слова «token», «CSRF», «invalid».
В некоторых случаях сервер может возвращать пустые ответы, как бы игнорируя запросы парсера.
Ни один из этих вариантов не может дать 100% гарантий, что вы столкнулись с баном из-за отсутствия CSRF-токена. Исключение – прямое указание на CSRF в тексте ошибки (если вы его получили, считайте, что вам сильно повезло).
Подробнее о том, как могут проявлять себя продвинутые системы защиты (WAF).
В качестве «эталонного образца» будет выступать поведение и ответы сервера при работе через классический браузер. То есть вам нужно обратиться к целевой странице вручную – из своего реального браузера, со всеми его настройками, куками и другими атрибутами цифрового отпечатка.
Когда вы убедились, что сайт работает «как надо», можно обращаться к странице или к API-интерфейсу из своего парсера и сравнивать результирующий ответ.
Если сервер возвращает ошибку или вообще ничего не возвращает, то с большой вероятностью вы не смогли правильно сымитировать работу реального клиента (в том числе, возможен отказ из-за отсутствия CSRF-токена).
Теперь вам нужно обратно вернуться в реальный браузер и детально изучить процесс обмена данными с сервером. Чаще всего нужно погрузиться в анализ POST-запросов (инструменты разработчика/DevTools → вкладка Сеть/Network).
На что конкретно обращать внимание:
Чтобы исключить или минимизировать риски возникновения ошибок скрапинга с CSRF-токенами, мы предлагаем следующие варианты.
Этот подход подразумевает следующие принципы:
Иными словами, если у вас есть POST-запросы, то парсер не может обращаться к страницам напрямую, без предварительного открытия сессии и получения cookie.
Не упустите из виду: токены, как и сессии, могут иметь свой срок жизни. Установить актуальный период действия можно только опытным путём. Самый распространённый диапазон значений – около 10-30 минут, но это сложно назвать «золотым стандартом». У каждого сайта может быть своя настройка времени действия – вплоть до привязки CSRF-токена к каждому новому запросу (активности пользователя).
Вы должны предусмотреть все типовые ситуации передачи CSRF-токена (в мета-тегах, в JSON/XML, в HTTP-заголовках, в формах, в виде JavaScript-переменных и т.п.) и уметь отдавать полученный токен в том формате, в котором его ожидают от браузера.
Если вы хотите быстрее обнаруживать CSRF-ошибки и недочёты при работе парсера с токенами, то желательно написать код, который бы логировал все действия парсера и ответов сервера. Так вам будет проще выявлять шаги, на которых вы получаете проблему.
В особо сложных случаях вы можете получать ошибки и баны не из-за CSRF-токенов, а из-за срабатывания систем защиты сайта. Ошибки, которые будет возвращать сервер, малоинформативные, а что ещё хуже – одинаковые в обоих случаях.
Поэтому, вместо того чтобы тратить время и силы на имитирование всего, что только можно, достаточно запустить парсинг через реальный браузер. Проблема с куками, а также с большинством других систем защиты отпадёт сама собой. Если и это не поможет, то нужно будет углубиться в имитирование поведения пользователя, а также позаботиться о скрытии следов работы headless-браузера.
Обратите внимание, при работе через headless и антидетект-браузеры, прокси должны ротироваться по-другому – на уровне экземпляра браузера, а не самого парсера.
Лучшие веб-драйвера: Playwright, Selenium, Puppeteer, Nodriver.
Пример простейшего парсера, который ищет 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-атак:
Ещё по теме: Лучшие практики для веб-скрапинга без блокировок.