Настройка защиты от CSRF (Cross-Site Request Forgery) на сайте
CSRF — атака, при которой злоумышленник заставляет авторизованного пользователя непреднамеренно выполнить действие на целевом сайте. Классический пример: пользователь залогинен в банк, открывает вредоносную страницу — та отправляет POST-запрос на перевод денег от его имени.
Механизм атаки
Браузер автоматически прикрепляет cookies к любому запросу на домен, независимо от того, откуда инициирован запрос. Если сайт использует сессионную аутентификацию через cookies, злоумышленник может создать форму или XHR, которые выполнят действие от имени жертвы.
Защита через CSRF-токены
Классический и наиболее надёжный подход: для каждой сессии генерируется уникальный токен, который встраивается в формы и проверяется при каждом POST/PUT/DELETE запросе.
Laravel (встроенная защита):
// Middleware VerifyCsrfToken подключён глобально в app/Http/Kernel.php
// В Blade-шаблонах:
<form method="POST" action="/profile">
@csrf
{{-- Генерирует: <input type="hidden" name="_token" value="..."> --}}
</form>
Для AJAX-запросов токен передаётся через мета-тег:
<meta name="csrf-token" content="{{ csrf_token() }}">
// Axios — настройка один раз глобально
axios.defaults.headers.common['X-CSRF-TOKEN'] =
document.querySelector('meta[name="csrf-token"]').content;
// Fetch
const response = await fetch('/api/data', {
method: 'POST',
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
SameSite Cookie
Современный и элегантный способ защиты — флаг SameSite в cookies:
// Laravel config/session.php
'same_site' => 'lax', // или 'strict'
| Значение | Поведение |
|---|---|
Strict |
Cookie не отправляется ни при каких cross-site запросах |
Lax |
Cookie не отправляется при POST cross-site, но отправляется при переходе по ссылке |
None |
Cookie отправляется везде (требует Secure) |
Lax — хороший баланс для большинства сайтов. Strict ломает переходы с внешних ссылок (пользователь кликает по ссылке на сайт из письма и оказывается неавторизованным).
Double Submit Cookie
Для API без сессий (SPA + JWT): токен хранится в cookie и дублируется в заголовке. Сервер сверяет их.
// Клиент читает cookie (доступен через JS, без HttpOnly)
const csrfToken = document.cookie
.split('; ')
.find(row => row.startsWith('XSRF-TOKEN='))
?.split('=')[1];
axios.defaults.headers.common['X-XSRF-TOKEN'] = csrfToken;
Laravel Sanctum использует именно этот паттерн для SPA-аутентификации.
Исключения из CSRF-защиты
Некоторые маршруты намеренно исключают из CSRF-проверки (webhooks от платёжных систем, Stripe events и т.д.):
// app/Http/Middleware/VerifyCsrfToken.php
protected $except = [
'stripe/*',
'webhooks/github',
];
Для excluded-маршрутов верификация подлинности выполняется через HMAC-подпись или Bearer-токен.
Проверка Origin/Referer
Дополнительный слой: проверка заголовка Origin или Referer при мутирующих запросах. Если Origin не совпадает с доменом сервера — запрос отклоняется.
// Пример middleware
public function handle($request, Closure $next)
{
$origin = $request->header('Origin');
$allowed = ['https://example.com', 'https://app.example.com'];
if ($request->isMethod('POST') && !in_array($origin, $allowed)) {
abort(403, 'Forbidden origin');
}
return $next($request);
}
Тестирование CSRF-защиты
- Создать HTML-страницу на другом домене с формой, отправляющей POST на защищаемый URL
- Авторизоваться на целевом сайте
- Перейти на вредоносную страницу и попытаться отправить форму
- Сервер должен вернуть 419 (Laravel) или 403
Срок реализации
- Базовая настройка CSRF-защиты в Laravel/Django/Rails: 1 день
- Интеграция с существующим SPA (Sanctum/XSRF cookie): 1–2 дня
- Аудит и исправление уязвимых форм в legacy-проекте: 2–5 дней







