Report 26.200.100: закрыта SQL-инъекция через формат имени пользователя
Обновление безопасности
Закрыта уязвимость или ослабленная проверка прав. Ставить в первую очередь.
Релиз «Конструктора отчётов» вендор описал фразой «Усилена безопасность модуля». На деле изменилась одна строка в CReport::getFormattedNameExpr(), теперь метод экранирует литералы из формата имени пользователя перед вставкой в SQL. Если модуль report установлен, обновите его до 26.200.100. Файл пошагового экспорта report.view/stexport.ajax.php отвечает на прямой запрос, поэтому неважно, пользуются ли выгрузкой на сайте.
Метод разбирает формат вида #LAST_NAME# #NAME_SHORT# на плейсхолдеры и литералы между ними и склеивает из них SQL-выражение для ФИО. Литералы вставлялись строковыми константами без экранирования, теперь их пропускают через ForSql():
-$expression[] = "'".$match."'";
+$expression[] = "'".$DB->ForSql($match)."'";
Формат приходит в метод через CReport::rewriteUserShortName() из компонента bitrix:report.view, параметр USER_NAME_FORMAT. Пошаговый экспорт (report.view/stexport.ajax.php) берёт параметры компонента, в том числе USER_NAME_FORMAT, из $_REQUEST['PARAMS']['COMPONENT_PARAMS']. Так литеральная часть формата могла прийти из запроса и попасть в SQL-выражение поля SHORT_NAME.
До правки формат проходил только через str_replace('%', '%%', ...). Это защищает sprintf-подстановку в выражении ORM, но не кавычки. Если вы сами собираете ExpressionField с литералом из настроек или запроса, нужны оба шага:
<?php declare(strict_types=1);
use Bitrix\Main\Application;
use Bitrix\Main\ORM\Fields\ExpressionField;
use Bitrix\Main\UserTable;
$helper = Application::getConnection()->getSqlHelper();
$rawSeparator = (string) Application::getInstance()->getContext()->getRequest()->get('sep');
// convertToDbString() экранирует и берёт в кавычки, %% нужен для sprintf внутри ExpressionField
$separator = str_replace('%', '%%', $helper->convertToDbString($rawSeparator));
$users = UserTable::query()
->setSelect(['ID', 'SHORT_NAME'])
->registerRuntimeField(new ExpressionField(
'SHORT_NAME',
$helper->getConcatFunction('%s', $separator, '%s'),
['LAST_NAME', 'NAME'],
))
->fetchAll();
Если склеивать ФИО в SQL незачем, выберите LAST_NAME и NAME через UserTable и соберите строку в PHP, тогда литерал в запрос не попадёт.
SQL-инъекцию в этом модуле закрывали и в прошлом релизе: report 26.200.0.