Защита от CSRF
CSRF (Cross-Site Request Forgery) — атака, при которой чужой сайт заставляет браузер посетителя отправить запрос к вашему сайту от его имени. Браузер приложит к такому запросу куки сессии, и для сайта он будет выглядеть как действие авторизованного пользователя: удаление сообщения, смена пароля, отправка формы.
Защита строится на токене, который знает только ваша страница: форма отправляет его вместе с данными, а сайт сверяет со значением из сессии. Чужой сайт токен получить не может, поэтому его запрос отклоняется.
Как это работает в JohnCMS
Проверку выполняет middleware, встроенный в конвейер обработки запроса. Он срабатывает раньше, чем запрос доходит до контроллера, и касается всех запросов небезопасными методами: POST, PUT, PATCH, DELETE. Запросы GET и HEAD не проверяются — они не должны менять состояние сайта.
Токен принимается двумя способами:
поле формы
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:
Пути сравниваются с нормализованным путём запроса (без завершающего слэша), допускаются маски в стиле оболочки.
Каждое исключение — это снятая защита. Прежде чем добавлять его, убедитесь, что запрос защищён иначе: подписью, ключом доступа, ограничением по адресу. Для запросов из своего же интерфейса исключение не нужно — достаточно передать заголовок.
Сессия и анонимные посетители
Токен создаётся вместе с сессией, поэтому страница с формой открывает сессию даже анонимному посетителю. Это учитывайте при настройке кеширования на стороне сервера.
Последнее обновление
Это было полезно?