Захист від CSRF
Вступ
Міжсайтова підробка запитів - це різновид зловмисної атаки, за якої від імені автентифікованого користувача виконуються несанкціоновані команди. На щастя, Laravel спрощує захист вашого застосунку від атак міжсайтової підробки запитів (CSRF).
Пояснення вразливості
Якщо ви не знайомі з міжсайтовою підробкою запитів, розгляньмо приклад того, як цю вразливість можна використати. Уявіть, що ваш застосунок має маршрут /user/email, який приймає POST-запит для зміни адреси електронної пошти автентифікованого користувача. Найімовірніше, цей маршрут очікує поле email із адресою, яку користувач хоче почати використовувати.
Без захисту від CSRF зловмисний сайт міг би створити HTML-форму, що вказує на маршрут /user/email вашого застосунку й надсилає власну адресу зловмисника:
<form action="https://your-application.com/user/email" method="POST">
<input type="email" value="malicious-email@example.com">
</form>
<script>
document.forms[0].submit();
</script>
Якщо зловмисний сайт автоматично надсилає форму під час завантаження сторінки, зловмиснику достатньо заманити нічого не підозрюючого користувача вашого застосунку на свій сайт - і адресу електронної пошти буде змінено.
Щоб запобігти цій вразливості, нам потрібно перевіряти кожен вхідний запит POST, PUT, PATCH чи DELETE на наявність секретного значення сесії, доступу до якого зловмисний застосунок не має.
Запобігання CSRF-запитам
Middleware Illuminate\Foundation\Http\Middleware\PreventRequestForgery, який за замовчуванням входить до групи web, захищає ваш застосунок від міжсайтової підробки запитів двошаровим підходом.
Спершу middleware перевіряє заголовок браузера Sec-Fetch-Site. Сучасні браузери автоматично встановлюють цей заголовок для кожного запиту, вказуючи, чи походить він із того самого джерела (origin), того самого сайту, чи з міжсайтового джерела. Якщо заголовок свідчить, що запит надійшов із того самого джерела, його одразу пропускають без жодної перевірки токена.
Якщо перевірка джерела не проходить - наприклад, тому що запит надходить зі старішого браузера, який не надсилає заголовок Sec-Fetch-Site, або тому що з'єднання не є захищеним, - middleware повертається до традиційної перевірки CSRF-токена.
Laravel автоматично генерує CSRF-«токен» для кожної активної сесії користувача, якою керує застосунок. Цей токен використовується, щоб перевірити, що запити до застосунку робить саме автентифікований користувач. Оскільки токен зберігається в сесії користувача й змінюється щоразу, коли сесію регенеровано, зловмисний застосунок не може отримати до нього доступ.
Доступ до CSRF-токена поточної сесії можна отримати через сесію запиту або через функцію-хелпер csrf_token:
use Illuminate\Http\Request;
Route::get('/token', function (Request $request) {
$token = $request->session()->token();
$token = csrf_token();
// ...
});
Щоразу, визначаючи у своєму застосунку HTML-форму з методом «POST», «PUT», «PATCH» чи «DELETE», додавайте до неї приховане поле CSRF _token, щоб middleware захисту від CSRF міг перевірити запит. Для зручності ви можете скористатися директивою Blade @csrf, яка згенерує приховане поле з токеном:
<form method="POST" action="/profile">
@csrf
<!-- Equivalent to... -->
<input type="hidden" name="_token" value="{{ csrf_token() }}" />
</form>
CSRF-токени та SPA
Якщо ви створюєте SPA, що використовує Laravel як бекенд для API, зверніться до документації Laravel Sanctum по інформацію про автентифікацію з вашим API та захист від CSRF-вразливостей.
Перевірка джерела
Як зазначено вище, middleware захисту від підробки запитів спершу перевіряє заголовок Sec-Fetch-Site, щоб визначити, чи надійшов запит із того самого джерела. За замовчуванням, якщо ця перевірка не проходить, middleware повертається до перевірки CSRF-токена.
Однак якщо ви хочете покладатися виключно на перевірку джерела й повністю вимкнути запасну перевірку токена, скористайтеся методом preventRequestForgery у файлі bootstrap/app.php вашого застосунку:
->withMiddleware(function (Middleware $middleware): void {
$middleware->preventRequestForgery(originOnly: true);
})
У режимі перевірки лише за джерелом запити, які не пройшли цю перевірку, отримають HTTP-відповідь 403 замість відповіді 419, яку зазвичай пов'язують із розбіжністю CSRF-токенів.
Заголовок
Sec-Fetch-Siteбраузери надсилають лише через захищені (HTTPS) з'єднання. Якщо ваш застосунок працює не через HTTPS, перевірка джерела буде недоступна, іmiddlewareповернеться до перевірки CSRF-токена.
Якщо ваш застосунок має приймати запити з піддоменів (наприклад, dashboard.example.com приймає запити від example.com), ви можете дозволити запити з того самого сайту на додачу до запитів із того самого джерела:
->withMiddleware(function (Middleware $middleware): void {
$middleware->preventRequestForgery(allowSameSite: true);
})
Виключення URI із захисту від CSRF
Іноді вам може знадобитися виключити набір URI із захисту від CSRF. Наприклад, якщо ви використовуєте Stripe для обробки платежів і користуєтеся їхньою системою вебхуків, вам доведеться виключити маршрут обробника вебхуків Stripe із захисту від CSRF, адже Stripe не знатиме, який CSRF-токен надсилати до ваших маршрутів.
Зазвичай такі маршрути варто розміщувати поза групою middleware web, яку Laravel застосовує до всіх маршрутів у файлі routes/web.php. Утім, ви також можете виключити конкретні маршрути, передавши їхні URI методу preventRequestForgery у файлі bootstrap/app.php вашого застосунку:
->withMiddleware(function (Middleware $middleware): void {
$middleware->preventRequestForgery(except: [
'stripe/*',
'http://example.com/foo/bar',
'http://example.com/foo/*',
]);
})
Для зручності CSRF-
middlewareавтоматично вимикається для всіх маршрутів під час запуску тестів.
X-CSRF-TOKEN
Крім перевірки CSRF-токена як POST-параметра, middleware PreventRequestForgery також перевіряє заголовок запиту X-CSRF-TOKEN. Ви можете, наприклад, зберігати токен у HTML-тегу meta:
<meta name="csrf-token" content="{{ csrf_token() }}">
Далі ви можете вказати бібліотеці на кшталт jQuery автоматично додавати токен до всіх заголовків запитів. Це дає простий і зручний захист від CSRF для ваших AJAX-застосунків, що використовують застарілі JavaScript-технології:
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
X-XSRF-TOKEN
Laravel зберігає поточний CSRF-токен у зашифрованій cookie XSRF-TOKEN, яка додається до кожної відповіді, згенерованої фреймворком. Ви можете використати значення цієї cookie, щоб задати заголовок запиту X-XSRF-TOKEN.
Цю cookie надсилають насамперед задля зручності розробників, оскільки деякі JavaScript-фреймворки та бібліотеки - як-от Angular і Axios - автоматично підставляють її значення в заголовок X-XSRF-TOKEN для запитів із того самого джерела.