Skip to content

Глава 5.4: Работа с CSRF-токенами

Время изучения: 30 минут


1. Что такое CSRF-атака? "Угон" вашего корабля

Представьте, что вы залогинены в панели управления вашим космическим флотом (space-api.test). В соседней вкладке вы открываете безобидный сайт с котиками (evil-cats.com). На этом сайте есть скрытая форма, которая автоматически отправляет запрос на ваш сайт по адресу POST /api/planets/1/delete.

Поскольку вы уже авторизованы на space-api.test, ваш браузер любезно прикрепит к этому запросу все ваши cookies. Сервер Laravel увидит валидную сессию и подумает, что это вы сами решили списать планету. Планета будет удалена без вашего ведома.

Это и есть CSRF (Cross-Site Request Forgery) — атака, при которой злоумышленник заставляет браузер аутентифицированного пользователя выполнить нежелательное действие на доверенном сайте.

💡 Космическая аналогия:

Вы — капитан корабля, и у вас есть ключ-карта (сессия/cookie). Злоумышленник не может украсть вашу карту. Но он может обманом заставить вас приложить ее к терминалу списания ресурсов, пока вы отвлеклись. CSRF-токен — это как пин-код, который нужно ввести вместе с картой. Злоумышленник не знает пин-код, и его атака проваливается.


2. Как Laravel защищает от CSRF?

Laravel по умолчанию защищает все "небезопасные" веб-запросы (POST, PUT, PATCH, DELETE) с помощью CSRF-токена.

  1. При генерации страницы Laravel создает уникальный, случайный токен для сессии пользователя.
  2. Этот токен встраивается в HTML-формы.
  3. При отправке формы токен уходит вместе с запросом.
  4. На сервере middleware VerifyCsrfToken сравнивает токен из запроса с токеном, хранящимся в сессии.
  5. Если токены не совпадают, Laravel прерывает запрос с ошибкой 419 (Session Expired/Page Expired).

Важно: API-маршруты в routes/api.php не защищены CSRF, так как они предполагают другой механизм аутентификации (например, токены Sanctum), а не сессии на основе cookies. Наша текущая проблема касается именно веб-маршрутов и страниц, которые мы создаем в routes/web.php.


3. Использование CSRF-токена в HTML-формах

Это самый простой сценарий. Laravel предоставляет для этого специальную Blade-директиву.

Пример: Форма для создания планеты Создадим простую форму в файле resources/views/planets/create.blade.php:

<h2>Форма запуска новой планеты</h2>
<form action="/planets" method="POST">
    @csrf {{-- Вот она, магия! --}}

    <label for="name">Название:</label>
    <input type="text" id="name" name="name" required>

    <label for="solar_system">Солнечная система:</label>
    <input type="text" id="solar_system" name="solar_system" required>

    {{-- ... другие поля ... --}}

    <button type="submit">Запустить</button>
</form>

Директива @csrf автоматически сгенерирует скрытое поле в форме:

<input type="hidden" name="_token" value="j2aK3dLf4gH5...уникальный_токен...">

Этого достаточно для защиты стандартных HTML-форм.


4. Использование CSRF-токена в AJAX/Fetch запросах

В прошлой главе мы отправляли DELETE запрос с помощью JavaScript. Сейчас Laravel будет его блокировать с ошибкой 419. Нам нужно добавить CSRF-токен в заголовки нашего Fetch-запроса.

Шаг 1: Сделать токен доступным для JavaScript

Добавьте мета-тег с токеном в <head> вашего мастер-макета resources/views/app.blade.php. Это стандартная практика в Laravel.

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    {{-- Добавляем CSRF-токен в мета-тег --}}
    <meta name="csrf-token" content="{{ csrf_token() }}">

    {{-- ... --}}
</head>

Функция csrf_token() возвращает текущий токен.

Шаг 2: Модифицируем JavaScript для отправки токена

Теперь в нашем public/js/planets.js мы можем прочитать этот токен и добавить его в заголовки всех "небезопасных" запросов.

// ... в файле public/js/planets.js ...

document.addEventListener('DOMContentLoaded', () => {
    // Получаем токен из мета-тега
    const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');

    const deleteButtons = document.querySelectorAll('.delete-btn');

    deleteButtons.forEach(button => {
        button.addEventListener('click', async (event) => {
            // ... логика подтверждения ...

            try {
                const response = await fetch(apiUrl, {
                    method: 'DELETE',
                    headers: {
                        'Accept': 'application/json',
                        'X-CSRF-TOKEN': csrfToken // <-- Добавляем токен в заголовки!
                    }
                });

                // ... остальная логика обработки ответа ...
            } catch (error) {
                // ...
            }
        });
    });
});
  • Имя заголовка X-CSRF-TOKEN — это стандарт, который Laravel проверяет по умолчанию.

Теперь наши AJAX-запросы тоже защищены. Попробуйте снова удалить планету — на этот раз запрос пройдет успешно.


Квиз для закрепления

1. Какую атаку предотвращает CSRF-токен?

2. Какая Blade-директива добавляет скрытое поле с CSRF-токеном в форму?

3. Что произойдет, если отправить POST-запрос без CSRF-токена на веб-маршрут?

4. Какой стандартный HTTP-заголовок используется для отправки CSRF-токена в AJAX-запросах?

5. Почему API-маршруты (`routes/api.php`) по умолчанию не используют CSRF-защиту?


🚀 Итог главы:

Вы установили "систему распознавания свой-чужой" на ваш космический корабль, защитив его от атак CSRF. Вы научились:

  • Понимать суть и опасность CSRF-атак.
  • Защищать стандартные HTML-формы с помощью директивы @csrf.
  • Передавать CSRF-токен в JavaScript через мета-тег.
  • Включать токен в заголовки AJAX/Fetch запросов для их успешного выполнения.

Ваши веб-интерфейсы теперь не только интерактивны, но и безопасны. В следующей главе мы завершим создание нашего веб-интерфейса, рассмотрев, как правильно организовать маршрутизацию для веб-страниц.