Класс CsrfGuard
Защита форм от подделки межсайтового запроса (CSRF).
Токен не хранится на сервере: он состоит из метки времени и HMAC-SHA256 от секрета по паре «метка времени плюс идентификатор сессии». Проверка - это пересчёт подписи, поэтому схема переживает перезапуск приложения и работает при нескольких рабочих процессах без общего хранилища.
Токен привязан к сессии: выданный одному посетителю не подойдёт другому. И ограничен по времени: подсмотренный однажды перестаёт работать.
Защита = View.СоздатьЗащитуCSRF(ПолучитьПеременнуюСреды("APP_SECRET"));
// то же самое напрямую
Защита = Новый CsrfGuard(ПолучитьПеременнуюСреды("APP_SECRET"));Конструктор
Новый CsrfGuard(Знач Секрет, Знач СрокЖизни = 3600)| Параметр | Тип | Описание |
|---|---|---|
Секрет | Строка | Секрет приложения. Одинаковый у всех рабочих процессов, разный в разных окружениях |
СрокЖизни | Число | Срок жизни токена в секундах |
Пустой секрет и неположительный срок жизни дают исключение: это ошибки настройки приложения, а не внештатные ситуации во время работы. Секрет в исходный код не помещается.
Свойства
| Свойство | Тип | Описание |
|---|---|---|
СрокЖизниТокена | Число | Срок жизни токена в секундах |
Методы
| Метод | Возвращает | Назначение |
|---|---|---|
СоздатьТокен | Строка | Токен для сессии |
ПроверитьТокен | Булево | Проверка токена |
ПолеФормы | Строка | Готовое скрытое поле формы |
ИмяПоля | Строка | _csrf |
ИмяЗаголовка | Строка | X-CSRF-Token |
ТокенИзЗаголовков | Строка | Токен из заголовков запроса |
СоздатьТокен
Функция СоздатьТокен(Знач ИдентификаторСессии, Знач МеткаВремени = Неопределено) ЭкспортВыдаёт токен для сессии.
Параметры
| Параметр | Тип | Описание |
|---|---|---|
ИдентификаторСессии | Строка | Идентификатор сессии посетителя |
МеткаВремени | Число | Время выдачи в секундах Unix. По умолчанию текущее время |
Возвращаемое значение: Строка - токен вида <метка времени>.<подпись>.
Пустой идентификатор сессии даёт исключение: токен без привязки к сессии не защищает ни от чего. Явная метка времени делает выдачу и проверку воспроизводимыми - это нужно в тестах и при разборе инцидентов.
Токен = Защита.СоздатьТокен(Запрос.Сессия.Идентификатор());
// 1785876773.940be1dbf278fd7cfd73657aab5195d8882616282dd36368527a4965f5a55e97ПроверитьТокен
Функция ПроверитьТокен(Знач Токен, Знач ИдентификаторСессии, Знач ТекущаяМетка = Неопределено) ЭкспортПроверяет токен. Не выбрасывает исключений: любое неожиданное значение - это просто непройденная проверка.
Параметры
| Параметр | Тип | Описание |
|---|---|---|
Токен | Строка | Проверяемый токен |
ИдентификаторСессии | Строка | Идентификатор сессии посетителя |
ТекущаяМетка | Число | Время проверки в секундах Unix. По умолчанию текущее время |
Возвращаемое значение: Булево - Истина, если токен выдан этой сессии и не истёк.
Проверка не проходит, если токен пустой или не строка, нет разделителя, метка времени не из цифр или длиннее 12 символов, метка из будущего, срок жизни истёк, подпись не совпала, идентификатор сессии пустой.
Подписи сравниваются за постоянное время, поэтому по длительности проверки подпись не подобрать. Метку ставит сервер по своим часам, поэтому токен из будущего считается поддельным.
Токен = Запрос.ПараметрыИменные[Защита.ИмяПоля()];
Если Не Защита.ПроверитьТокен(Токен, Запрос.Сессия.Идентификатор()) Тогда
Ответ.УстановитьСостояние(403);
Возврат;
КонецЕсли;ПолеФормы
Функция ПолеФормы(Знач ИдентификаторСессии) ЭкспортГотовое скрытое поле формы с токеном. Значения экранируются, поле вставляется в шаблон как есть.
Параметры
| Параметр | Тип | Описание |
|---|---|---|
ИдентификаторСессии | Строка | Идентификатор сессии посетителя |
Возвращаемое значение: Строка - разметка input type="hidden".
Модель.Вставить("ПолеCSRF", Защита.ПолеФормы(Запрос.Сессия.Идентификатор()));<form hx-post="/задачи">
{{ Модель.ПолеCSRF }}
<input name="название">
</form>ИмяПоля
Функция ИмяПоля() ЭкспортВозвращаемое значение: Строка - _csrf. Имя поля формы, в котором ожидается токен.
Токен = Запрос.ПараметрыИменные[Защита.ИмяПоля()];ИмяЗаголовка
Функция ИмяЗаголовка() ЭкспортВозвращаемое значение: Строка - X-CSRF-Token. Имя заголовка, в котором ожидается токен.
Для запросов htmx токен удобно передавать заголовком сразу для всех форм страницы:
<body hx-headers='{"X-CSRF-Token": "{{ Модель.ТокенCSRF }}"}'>ТокенИзЗаголовков
Функция ТокенИзЗаголовков(Знач Заголовки) ЭкспортИзвлекает токен из заголовков запроса независимо от регистра имени.
Параметры
| Параметр | Тип | Описание |
|---|---|---|
Заголовки | Соответствие, Структура | Заголовки запроса |
Возвращаемое значение: Строка - значение заголовка либо пустая строка.
Если Не Защита.ПроверитьТокен(Защита.ТокенИзЗаголовков(Запрос.Заголовки), Сессия.Идентификатор()) Тогда
Ответ.УстановитьСостояние(403);
КонецЕсли;