Полный цикл в digital

SQL инъекции в Битрикс

SQL-инъекция — уязвимость, которая позволяет внедрить вредоносный код через пользовательский ввод. В Bitrix Framework есть два способа работы с базой данных, каждый требует особого подхода к защите:

  1. Прямые запросы. SQL-запросы выполняются напрямую методами класса CDatabase или через метод Application::getConnection. Разработчик контролирует формирование запросов, поэтому важно тщательно проверять и экранировать пользовательский ввод, чтобы избежать SQL-инъекций
  2. ORM. Объектная модель снижает риск SQL-инъекций, так как она автоматически обрабатывает данные и защищает от внедрения вредоносного кода. В ORM необходимо проверять права доступа и данные, которые попадают в базу

Прямые запросы

Класс CDatabase

Чтобы SQL-запросы были безопасными используйте:

Приведение типов для чисел

Преобразуйте ввод в число, если ожидается числовое значение:

// опасный SQL запрос
SELECT * FROM b_user WHERE id=$_REQUEST['id'];

// безопасный вариант
$id = intval($_REQUEST['id']);
$res = $DB->Query("SELECT * FROM b_user WHERE id=$id");
Экранирование строк

Метод ForSql() экранирует строки. Результат нужно заключить в кавычки:

// опасный запрос
$login = $_REQUEST['login'];
$res = $DB->Query("SELECT * FROM b_user WHERE LOGIN='$login'");

// безопасный запрос
$login = $DB->ForSql($_REQUEST['login']);
$res = $DB->Query("SELECT * FROM b_user WHERE LOGIN='$login'");
Подготовленные выражения

Для безопасной работы с выражениями используйте методы CDatabase:

  1. PrepareInsert для вставки данных
  2. PrepareUpdate для обновления данных

Методы выполняют все необходимые преобразования входных данных:

// код безопасно вставляет в таблицу пользовательский ввод
$arInsert = $DB->PrepareInsert("b_user", ["LOGIN" => $_REQUEST["login"]]);
$sql = "INSERT INTO b_user (".$arInsert[0].") VALUES (".$arInsert[1].")";
$res = $DB->Query($sql);

Ключи с префиксом ~ не обрабатываются автоматически. Их могут использовать для внедрения вредоносного кода. Убедитесь, что такие ключи содержат только проверенные значения.

Метод Application:getConnection

$connection = \Bitrix\Main\Application::getConnection();
$helper = $connection->getSqlHelper(); // получаем хелпер для экранирования

$login = $helper->forSql($_GET['login']); // очищаем данные
$res = $connection->query("SELECT * FROM b_user WHERE LOGIN='$login'");
  1. Метод Application::getConnection возвращает объект MysqliConnection для выполнения запросов к базе данных
  2. Функция getSqlHelper() получает хелпер для экранирования запросов
  3. Функция forSql объекта хелпера очищает данные

Аналогично CDatabase, можно использовать методы prepareInsert и prepareUpdate.

ORM

ORM автоматически защищает от SQL-инъекций при работе с базой данных. Однако существуют другие риски:

  1. XSS. В базу данных может попасть HTML или JS-код
  2. IDOR. Пользователь может получить доступ к данным из-за неверной проверки прав

Параметры select и filter метода getlist уязвимы, если получают на вход пользовательские данные. Это позволяет получить записи как из основной таблицы, так и из связанных таблиц.

Параметр select

Параметр select указывает, какие поля нужно выбрать из базы данных. Его можно задать двумя способами:

// в методе getList
$result = \Bitrix\Landing\Internals\LandingTable::getList([
    'select' => $some_select
]);

// с помощью setSelect
$query = WorkgroupTable::query();
$query->setSelect($select);

Непроверенный ввод позволяет получить данные:

  • Из любых неприватных столбцов текущей таблицы
  • Из связанных таблиц через ReferenceField
// прямая подстановка пользовательского ввода
$select = $_REQUEST['select']

//['ID' => 'ID', 'TITLE' => 'TITLE', 'LAST_NAME' => 'RESPONSIBLE.LAST_NAME'];

$result = \Bitrix\Tasks\Internals\TaskTable::getList([
    'select' => $select // доступ к любым поля и связям
]);
var_dump($result->fetchAll());

// результат может включать данные из связанных таблиц
/*  ...
    array(3) {
    ["ID"]=>
    string(1) "1"
    ["TITLE"]=>
    string(4) "Task Title"
    ["LAST_NAME"]=>
    string(20) "Lastname"
    ...
  }
*/

Параметр filter

При наличии ReferenceField можно достать данные из связанных таблиц через параметр filter:

// Пример подбора email через фильтр
$result = \Bitrix\Main\UserTable::getList([
    // если что-то возвращается, значит email начинается с буквы А. Если нет — пробуем другой символ.
    'filter' => ['ID' => 1, '%=EMAIL' => "A%"]
]);

Приватные поля в ORM защищены, доступ к ним ограничен. Частичная разметка базы данных может привести к утечке.

Выражения SqlExpression и ExpressionField

Класс \Bitrix\Main\DB\SqlExpression позволяет создавать пользовательские SQL-выражения. Один непроверенный параметр приведет к инъекции:

// $_GET['id'] содержит вредоносный код
$filterIds = [1, 2, 3, $_GET['id']];

//$_GET['id'] = (select * from b_user where login="admin")

$filterList = \Bitrix\Disk\Internals\VolumeTable::getList([
   'filter' => [
      '@ID' => new \Bitrix\Main\DB\SqlExpression(implode(', ', $filterIds)),
      // может привести к выполнению произвольного SQL-кода
   ],
]);

Класс \Bitrix\Main\Entity\ExpressionField создает вычисляемые поля. Как и SqlExpression, он уязвим к непроверенному вводу:

$filterList = \Bitrix\Disk\Internals\VolumeTable::getList([
   'runtime' => array(
      new \Bitrix\Main\Entity\ExpressionField('CNT', $_GET['count'])
      // позволяет внедрить вредоносный код
   )
]);

При использовании классов SqlExpression и ExpressionField необходимо тщательно проверять и экранировать пользовательский ввод, чтобы предотвратить SQL-инъекции и защитить приложение от выполнения нежелательного кода. Не передавайте данные напрямую — обрабатывайте их с помощью методов экранирования.

Заполните форму уже сегодня!
Для начала сотрудничества необходимо заполнить заявку или заказать обратный звонок. В ответ получите коммерческое предложение, которое будет содержать индивидуальную стратегию с учетом требований и поставленных задач
Работаем по будням с 9:00 до 18:00. Заявки, отправленные в выходные, обрабатываем в первый рабочий день до 12:00.
Спасибо, ваш запрос принят и будет обработан!