Налаштування захисту від CSRF для сайту
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.ua', 'https://app.example.ua'];
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 днів







