Cheat sheet: предотвращение SQL-инъекций

Эта “шпаргалка” по предотвращению SQL-инъекций предоставляет практические стратегии для защиты веб-приложений от одной из самых критических угроз безопасности. Она объясняет типы атак, демонстрирует безопасные шаблоны написания кода и делает упор на тестирование и мониторинг для надежной защиты.

Типы SQL-инъекций

SQL-инъекции – это широкий класс недостатков безопасности, позволяющих злоумышленнику добавлять запросы в базу данных или команды во входные данные так, что сервер базы данных их выполняет. Существуют разные типы SQLi в зависимости от того, где и как внедряется SQL-код:

  • Обычная (“in-band”) SQL-инъекция: прямой ввод вредоносного SQL во встроенные запросы. Это включает как SQL-инъекции на основе ошибок (“error-based”), так и “union-based”.
  • Слепая (“blind”) SQL-инъекция: получение данных с помощью boolean логики или техник на основе времени, когда не отображаются сообщения об ошибках. Обычно опирается на наблюдение за поведением страницы, содержимым ответов или задержками времени.
  • Out-of-band SQL-инъекция: использование таких методов, как DNS- или HTTP-запросы, для получения данных, когда прямые ответы недоступны.
  • SQL-инъекция второго порядка (“second order”): внедрение полезной нагрузки, которая запускает SQL-инъекцию позже в рабочем процессе программы. Часто пропускается при тестировании, поскольку выполняется не сразу.

Лучшие практики для предотвращения SQL-инъекций

Избегание создания SQL-запросов с помощью конкатенации строк

Непосредственное включение данных пользователя в строку запроса опасно по своей сути. Каждый раз, когда они объединяются с запросом в базу данных, данные и логика программы смешиваются, оставляя злоумышленнику пространство для контроля поведения приложения. Опасный конкатенированный запрос в PHP может выглядеть так:

$query = "SELECT * FROM users WHERE userid = " . $_GET['userid'];

Если злоумышленник может ввести SQL-код как значение параметра userid, программа уязвима к SQL-инъекциям.

Использование параметризованных запросов или подготовленных операторов (statements)

Основное правило для предотвращения SQL-инъекций – никогда не вставлять входные данные пользователя непосредственно в SQL-код. Независимо от того, работает ли команда с MySQL, PostgreSQL, Oracle или Microsoft SQL Server, важно всегда использовать параметризованные запросы или подготовленные операторы. Они отделяют вводимые пользователем данные от синтаксиса SQL, гарантируя, что даже специальные символы не смогут изменить логику запроса.

Вот пример создания подготовленного оператора на Java:

// Следует убедиться, что conn является действительным открытым соединением, и правильно обрабатывает исключения в коде продакшна
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE userid = ?");
stmt.setInt(1, userId); // $userId содержит значение, введенное пользователем
ResultSet rs = stmt.executeQuery();

Подобный пример для PHP с использованием PDO:

// Следует применить PDO::ATTR_ERRMODE для правильной обработки ошибок
$stmt = $pdo->prepare("SELECT * FROM users WHERE userid = :userid");
$stmt->execute(['userid' => $userId]); // $userId содержит значение, введенное пользователем

Проверка входных данных

Параметризация останавливает инъекцию, но проверка входных данных также важна для повышения устойчивости, предотвращая как попытки атаки, так и ошибки ввода данных. Важно использовать белые списки для ограничения входных данных для известных закрытых параметров. Также следует очищать входные данные, чтобы проверить, что целые числа действительно являются целыми числами, и что строки соответствуют ожидаемым шаблонам, а неожиданные значения, такие как NULL или специальные символы, обрабатываются корректно. Например, чтобы обеспечить целочисленные значения в PHP, можно использовать:

if (!filter_var($userid, FILTER_VALIDATE_INT))  {
    throw new Exception("Invalid userid");
}

Это устраняет риск потенциально вредных значений строки в параметре userid. Однако это только проверяет формат данных и не заменяет параметризованные запросы.

Осторожность с сохраненными процедурами

Сохраненные процедуры могут повысить безопасность, но только если они используют параметризованные входные данные и избегают динамического SQL. Если применяются какие-либо динамические скрипты внутри процедуры, это повышает риск инъекции. Например, в Oracle можно настроить процедуру получения пользователя по идентификатору, а затем вызвать процедуру вместо выполнения запроса из кода программы:

CREATE PROCEDURE GetUser(p_userid IN NUMBER) AS
BEGIN
    SELECT * FROM users WHERE userid = p_userid;
END;

Безопасная обработка ошибок

Следует избегать предоставления подробных сообщений об ошибках, раскрывающих названия таблиц, столбцов или структуру запроса. Нужно возвращать сообщения об ошибках пользователям и создавать логи деталей на стороне сервера. Как дополнительное преимущество, стандартизация обработки ошибок и сообщений может предотвратить или воспрепятствовать некоторым атакам на основе времени.

Мониторинг и создание логов запросов

Мониторинг помогает выявлять необычные паттерны запросов, такие как повторяющиеся логические условия или задержки на основе времени, которые могут свидетельствовать об атаках слепой SQL-инъекции. Также следует искать неожиданные SQL-команды или неудачные попытки аутентификации в логах, охватывающих как журнал на уровне базы данных, так и мониторинг на уровне программы. Как более общая мера безопасности, конфиденциальные данные никогда не должны записываться в логи.

Обновление технологического стека

Важно регулярно обновлять не только компоненты собственного приложения, но и объектно-реляционную проекцию (ORM), драйверы баз данных, программное обеспечение СУБД и операционные системы бэкенда. Это касается как платформ с открытым кодом, так и коммерческих. Поддержка актуальности помогает минимизировать уязвимости.

Принцип наименьших привилегий

Хотя это не предотвращает атаки, программа должна подключаться к серверу базы данных с помощью учетной записи с низкими привилегиями и иметь доступ только к таблицам и столбцам, необходимым для ее работы. Это помогает минимизировать влияние и ограничить эскалацию, если злоумышленник где-то найдет и использует уязвимость SQLi. Аналогично, сервер базы данных должен работать только с минимально необходимыми разрешениями и функциями, чтобы ограничить возможности злоумышленника.

Осторожное использование объектно-реляционной проекции (ORM)

Системы объектно-реляционной проекции (Object-relational mapping, ORM) обычно используются для взаимодействия между объектно-ориентированными языками программирования (чаще всего Java) и реляционными базами данных. Хотя современные ORM могут полностью абстрагироваться от языка запросов и уменьшить риск инъекций, все равно надо знать функции параметризации ORM и правильно их использовать. Если возвращаться к сырым SQL-запросам, то нужно обеспечивать их безопасность так же, как и без ORM вообще.

Написание безопасного кода и сканирование на уязвимости

Применение всех этих рекомендаций в значительной степени поможет избежать SQL-инъекций в приложениях, но единственный способ убедиться в этом – это тестирование. Их следует сканировать на наличие уязвимостей безопасности, включая SQL-инъекции, и тестировать на нескольких этапах жизненного цикла разработки ПО (SDLC).

Инструменты статического тестирования безопасности приложений (SAST) могут выявлять некоторые из самых очевидных рисков уже на ранних этапах написания кода, но убедиться, что запущенное приложение не имеет эксплуатируемых уязвимостей – это задача динамического тестирования безопасности приложений (DAST). С помощью передовых инструментов, таких как Invicti (ранее Netsparker), можно выполнять проверки безопасности в пайплайне и продакшне, чтобы обнаружить широкий спектр SQL-инъекций, включая out-of-band уязвимости. А благодаря proof-based сканированию с подтверждением эксплуатации можно убедиться в существовании проблемы.

Вывод

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

Если вы хотите бесплатно протестировать Invicti (DAST), оставьте свою контактную информацию в форме ниже:

Запрос на бесплатное тестирование Invicti

Оставьте контакты и мы с вами свяжемся

Подписаться на новости