Ошибка проверки источника запроса - разбор причин
Форма в админке или действие на сайте не сохраняются, а вместо результата приходит строка «Ошибка проверки источника запроса». Разбираем причины по убыванию частоты, начиная с подписи сеанса.
С чего начать
Сравниваем пришедшую подпись с ожидаемой:
// временно в начале обработчика, до изменяющей данные логики$sent = $_REQUEST['sessid'] ?? $_SERVER['HTTP_X_BITRIX_CSRF_TOKEN'] ?? '';var_dump($sent, bitrix_sessid()); // что прислал браузер и чего ждёт сеансvar_dump(check_bitrix_sessid()); // false - значения разошлись или подписи нетЭта пара значений делит все причины пополам. Пустая строка слева означает, что подпись до сервера не дошла, а два разных непустых значения - что сессия сменилась между открытием страницы и отправкой формы.
Смотрим, что лежит в разметке формы:
curl -s 'https://example.com/personal/profile/' | grep -o 'name="sessid" value="[^"]*"'# поле есть, а value пустое - страница отдана из кэша композита# реальное значение туда подставляет клиентский код, и это штатное поведениеПустое поле подписи в кэшируемом шаблоне не поломка, а следствие правила: серверные переменные в такой шаблон не попадают. Форма без клиентского кода уходит на сервер с пустой подписью и получает отказ.
Ставим подпись в свою форму:
<form method="post" action="/local/tools/save.php"> <?= bitrix_sessid_post() ?> <!-- скрытое поле sessid, первым в форме --> <input type="text" name="phone"></form>Вызов размещают как можно выше: разметка, вставленная перед подписью, способна
увести скрытое поле во внешнюю ссылку. Для ссылок с изменяющим действием берут
парный вызов bitrix_sessid_get(), но лучше переводить их на метод POST.
Передаём подпись из скрипта:
fetch('/local/tools/save.php', { method: 'POST', headers: { 'X-Bitrix-Csrf-Token': BX.message('bitrix_sessid') }, body: new FormData(document.forms.feedback)});// BX.bitrix_sessid() возвращает то же значение и годится вместо сообщенияПроверка сверяет с текущей подписью либо параметр запроса, либо этот заголовок. Значение читают на клиенте, а не печатают из PHP: напечатанное однажды застынет в кэше страницы и переживёт смену сессии.
Проверяем, доехала ли cookie сеанса:
$request = \Bitrix\Main\Context::getCurrent()->getRequest();var_dump($request->getCookie('PHPSESSID')); // null - cookie до сайта не доехалаvar_dump($_SERVER['HTTP_HOST'], session_id()); // домен запроса и текущий сеанс// cookie выдаётся на один домен, и адрес сайта обязан совпасть с настройкойCookie сеанса выдаётся ровно на тот домен, который прописан в настройках главного модуля. После переезда на новый адрес старое значение этого поля отсекает cookie, и сверять подпись оказывается уже не с чем.
Смотрим, где сайт хранит сессии:
grep -A6 "'session'" bitrix/.settings.php # type: file, redis, memcache, databasephp -i | grep -E 'session.save_path|session.cookie_domain'# при нескольких веб-серверах хранение в файлах теряет сессию на соседней нодеХранилище сессий объясняет случаи, где отказ приходит через раз. Запрос, ушедший на соседнюю ноду, своей сессии там не находит, и подпись отклоняется без всякой связи с самой формой.
Причины
-
Своя форма или свой запрос уходят без подписи примерно 30% случаев
ПризнакОтказ приходит только на самодельной форме или на своём файле обработчика.
ПроверкаСмотрим разметку формы и тело запроса: есть ли поле подписи или заголовок с ней.
Что делатьСтавим подпись штатным вызовом в начало формы, а в запрос из скрипта - заголовком.
-
Подпись пришла пустой из кэшируемой области примерно 22% случаев
ПризнакВ исходном коде страницы скрытое поле подписи есть, но значение у него пустое.
ПроверкаЗапрашиваем страницу без браузера и смотрим значение скрытого поля подписи.
Что делатьБерём значение на клиенте сообщением платформы либо выносим форму в динамическую зону.
-
Сессия сменилась между открытием и отправкой примерно 18% случаев
ПризнакФорма пролежала открытой долго либо в соседней вкладке был вход или выход.
ПроверкаСверяем подпись из разметки открытой страницы с текущим значением подписи сеанса.
Что делатьПерезагружаем страницу перед отправкой, а долгие формы обновляют подпись запросом.
-
Сессия не находится на соседнем веб-сервере примерно 15% случаев
ПризнакОтказ приходит через раз, а повторная отправка того же действия проходит нормально.
ПроверкаСмотрим настройку хранения сессий и число веб-серверов за балансировщиком.
Что делатьПереносим сессии в общее хранилище либо привязываем клиента к ноде на балансировщике.
-
Cookie сеанса не долетает до сервера примерно 10% случаев
ПризнакОтказ начался после смены домена, переезда на поддомен или ужесточения атрибутов cookie.
ПроверкаСверяем доменное имя в настройках главного модуля с адресом, по которому открыт сайт.
Что делатьПриводим доменное имя к рабочему адресу и ослабляем строгий атрибут межсайтовых cookie.
-
Проактивная защита сменила идентификатор сессии примерно 5% случаев
ПризнакОтказы пошли сразу после повышения уровня защиты или смены хранилища сессий.
ПроверкаСмотрим уровень проактивной защиты и журнал событий главного модуля за эти сутки.
Что делатьЗащиту оставляем включённой, а подпись в формах и запросах подтягиваем на клиенте.
Частые вопросы
Как правильно использовать bitrix_sessid_post и check_bitrix_sessid?
В форму ставят вызов, который создаёт скрытое поле с подписью сеанса. В обработчике проверку выполняют первой строкой: не сошлось - прекращают обработку до изменения данных.
Как получить подпись в JavaScript без библиотек Битрикса?
Значение лежит в скрытом поле формы, и его читают обычным запросом к разметке страницы. С подключённым ядром надёжнее сообщение платформы: оно обновляется вместе с сессией.
Почему в исходном коде страницы поле подписи пустое?
Страница отдана из кэша композита, а серверные переменные в кэшируемый шаблон не попадают. Значение подставляет клиентский код, и это штатное поведение платформы.
Ошибка появилась после смены доменного имени, что смотреть?
Доменное имя в настройках главного модуля и адрес сайта в списке сайтов. Cookie сеанса выдаётся на конкретный домен, и при расхождении подпись до сервера не доезжает.
Та же строка приходит при обмене с 1С - это одно и то же?
Симптом общий, а сценарий другой: обмен авторизуется своим способом и держит сеанс в cookie. Такой отказ разбирают по странице про ответ точки обмена, а не здесь.
Смежное
- Ошибки сервера и базы - оглавление подтемы
- Could not start session by PHP: сессия не стартует - когда сессии нет вовсе
- Данные от посетителя: экранирование, проверка, сеанс - как ставить проверку сеанса в своей форме
- Не работает AJAX-запрос: разбор причин - другие причины отказа запроса, кроме подписи
- Отправка формы через AJAX: проверка полей, защита, ответ с ошибками - готовая форма с подписью и разбором ответа
- Слетает авторизация: время жизни сессии и запоминание входа - почему сеанс кончается раньше ожидаемого
- Композит изнутри: статическая копия, зоны, пересборка - откуда в кэше берётся пустое поле
- Веб-кластер: репликация, memcached, сессии - общее хранилище сессий для нескольких нод
- 1С не может прочитать ответ сайта: разбор причин - та же строка со стороны обмена
- Инфраструктура и эксплуатация - устройство серверной части целиком