Предотвращение атак CSRF
Введение
Межсайтовая подделка запроса – это разновидность вредоносного эксплойта, при котором неавторизованные команды выполняются от имени аутентифицированного пользователя. К счастью, Laravel позволяет легко защитить ваше приложение от Межсайтовой подделки запроса (Cross Site Request Forgery – 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
Посредник Illuminate\Foundation\Http\Middleware\PreventRequestForgery, который по умолчанию включен в группу посредников web, защищает ваше приложение от подделки межсайтовых запросов с помощью двухуровневого подхода.
Сначала посредник проверяет заголовок браузера Sec-Fetch-Site. Современные браузеры автоматически устанавливают этот заголовок для каждого запроса, указывая, был ли он отправлен из того же источника, с того же сайта или из межсайтового источника. Если заголовок показывает, что запрос пришел из того же источника, запрос сразу разрешается без проверки токена.
Если проверка источника не проходит – например, потому что запрос пришел из старого браузера, который не отправляет заголовок Sec-Fetch-Site, или потому что соединение не защищено, – посредник возвращается к традиционной проверке 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» в своем приложении, вы должны включать в форму скрытое поле _token CSRF, чтобы посредник CSRF мог проверить запрос. Для удобства вы можете использовать директиву Blade @csrf для создания скрытого поля ввода, содержащего токен:
<form method="POST" action="/profile">
@csrf
<!-- Эквивалентно... -->
<input type="hidden" name="_token" value="{{ csrf_token() }}" />
</form>
CSRF-токены и SPA-приложения
Если вы создаете SPA, который использует Laravel в качестве серверной части API, вам следует обратиться к документации Sanctum для получения информации об аутентификации с помощью вашего API и защите от уязвимостей CSRF.
Проверка источника
Как обсуждалось выше, посредник Laravel для защиты от подделки запросов сначала проверяет заголовок Sec-Fetch-Site, чтобы определить, пришел ли запрос из того же источника. По умолчанию, если эта проверка не проходит, посредник возвращается к проверке CSRF-токена.
Однако, если вы хотите полагаться только на проверку источника и полностью отключить fallback к CSRF-токену, вы можете сделать это с помощью метода preventRequestForgery в файле bootstrap/app.php вашего приложения:
->withMiddleware(function (Middleware $middleware): void {
$middleware->preventRequestForgery(originOnly: true);
})
При использовании режима только по источнику запросы, не прошедшие проверку источника, получат HTTP-ответ 403 вместо ответа 419, который обычно связан с несовпадением CSRF-токена.
Заголовок
Sec-Fetch-Siteотправляется браузерами только через защищенные соединения (HTTPS). Если ваше приложение обслуживается не через HTTPS, проверка источника будет недоступна, и посредник вернется к проверке CSRF-токена.
Если вашему приложению нужно принимать запросы с поддоменов (например, dashboard.example.com принимает запросы с example.com), вы можете разрешить запросы с того же сайта в дополнение к запросам из того же источника:
->withMiddleware(function (Middleware $middleware): void {
$middleware->preventRequestForgery(allowSameSite: true);
})
Исключение URI из защиты от CSRF
По желанию можно исключить набор URI из защиты от CSRF. Например, если вы используете Stripe для обработки платежей и используете их систему веб-хуков, вам нужно будет исключить маршрут обработчика веб-хуков Stripe из защиты от CSRF, поскольку Stripe не будет знать, какой токен CSRF отправить вашим маршрутам.
Как правило, вы должны размещать эти виды маршрутов вне группы посредников 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 автоматически отключается для всех маршрутов при выполнении тестов.
X-CSRF-TOKEN
В дополнение к проверке токена CSRF в качестве параметра POST-запроса посредник PreventRequestForgery также проверяет заголовок запроса X-CSRF-TOKEN. Вы можете, например, сохранить токен в HTML-теге meta:
<meta name="csrf-token" content="{{ csrf_token() }}">
Затем, вы можете указать библиотеке, такой как jQuery, автоматически добавлять токен во все заголовки запросов. Это обеспечивает простую и удобную защиту от CSRF для ваших приложений с использованием устаревшей технологии JavaScript на основе AJAX:
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
X-XSRF-TOKEN
Laravel хранит текущий токен CSRF в зашифрованном файле Cookies XSRF-TOKEN, который содержится в каждом ответе, генерируемым фреймворком. Вы можете использовать значение Cookies для установки заголовка запроса X-XSRF-TOKEN.
Этот файл Cookies, в первую очередь, отправляется для удобства разработчика, поскольку некоторые фреймворки и библиотеки JavaScript, такие, как Angular и Axios, автоматически помещают его значение в заголовок X-XSRF-TOKEN в запросах с одним и тем же источником.