For the complete documentation index, see llms.txt. This page is also available as Markdown.

Защита от CSRF

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

Защита строится на токене, который знает только ваша страница: форма отправляет его вместе с данными, а сайт сверяет со значением из сессии. Чужой сайт токен получить не может, поэтому его запрос отклоняется.

Как это работает в JohnCMS

Проверку выполняет middleware, встроенный в конвейер обработки запроса. Он срабатывает раньше, чем запрос доходит до контроллера, и касается всех запросов небезопасными методами: POST, PUT, PATCH, DELETE. Запросы GET и HEAD не проверяются — они не должны менять состояние сайта.

До версии 10.0 токен проверялся правилом валидации Csrf, которое каждый контроллер подключал сам. Теперь проверка общая, а правило удалено: писать её в контроллере не нужно и не следует.

Токен принимается двумя способами:

  • поле формы csrf_token;

  • заголовок X-CSRF-Token — для запросов, у которых нет формы (AJAX с телом в JSON, загрузка файлов из редактора).

Токен в форме

В каждую форму, которая отправляется методом POST, добавьте скрытое поле:

<form action="{{ url }}" method="post">
    <input type="hidden" name="csrf_token" value="{{ app.csrf_token }}">
    ...
</form>

Значение app.csrf_token доступно в любом шаблоне сайта. Если формы нет — например, действие выполняется ссылкой, — сделайте маленькую форму с кнопкой: запрос, меняющий данные, не должен выполняться по GET.

Токен в запросах JavaScript

Макеты темы печатают токен в мета-теге:

В стандартной теме axios настроен так, что подставляет заголовок во все запросы автоматически — писать ничего не нужно. Если вы отправляете запросы иначе, возьмите токен из мета-тега сами:

Отдельный случай — загрузка файлов редактором CKEditor: он отправляет собственный запрос, поэтому токен передаётся ему в настройках:

Что происходит при неверном токене

Запрос не доходит до контроллера и получает ответ 403. Форма ответа зависит от того, кто спрашивает:

  • обычный запрос из браузера — страница ошибки с текстом о том, что сессия устарела и страницу нужно обновить;

  • AJAX-запрос (X-Requested-With: XMLHttpRequest) или запрос с Accept: application/json — ответ JSON вида {"message": "…"}, который скрипт может показать пользователю.

Каждый отклонённый запрос записывается в лог с URL, методом и заголовком Referer — по логу удобно искать формы, в которых забыли токен.

Исключения

Иногда проверку нужно отключить: например, эндпоинт принимает запросы от внешнего сервиса, у которого токена нет и быть не может.

На маршруте

Основной способ. Исключение объявляется рядом с маршрутом, поэтому его видно при чтении конфигурации модуля:

Метод есть и у группы маршрутов — тогда он действует на все маршруты внутри, включая вложенные группы:

В конфигурации

Для точек входа, маршруты которых вы не редактируете, есть список путей в config/csrf.php:

Пути сравниваются с нормализованным путём запроса (без завершающего слэша), допускаются маски в стиле оболочки.

Сессия и анонимные посетители

Токен создаётся вместе с сессией, поэтому страница с формой открывает сессию даже анонимному посетителю. Это учитывайте при настройке кеширования на стороне сервера.

Последнее обновление

Это было полезно?