Проанализируйте следующий SQL‑фрагмент, потенциально уязвимый к инъекциям: String q = "SELECT * FROM users WHERE name = '" + name + "'"; — объясните, как атакующий может воспользоваться уязвимостью, какие техники предотвращения существуют и как их правильно применить в разных СУБД
Кратко о проблеме - Фрагмент String q = "SELECT * FROM users WHERE name = '" + name + "'"; — формирует SQL путём конкатенации данных пользователя и кода: это классическая SQL‑инъекция. Любой символ из переменной name попадёт в запрос без проверки/экранирования. Как атакующий может воспользоваться (примеры) - Тавтология (вернёт все записи): если name = "' OR 1=11=11=1 -- " → запрос станет SELECT * FROM users WHERE name = '' OR 1=11=11=1 -- ' (комментарий отсекает остаток). - UNION‑инъекция (получить другие столбцы): name = "' UNION SELECT password FROM users -- " → позволяет слить результат другого SELECT. - Закрытие строки + дополнительные команды (stacked queries): name = "'; DROP TABLE users; -- " (работает, если драйвер/СУБД разрешают множественные запросы). - Blind‑инъекции: побитовые/временные техники, если видно только true/false или задержку (например, name = "' OR (SELECT IF(SUBSTR(password,1,1)='a', SLEEP(5), 0)) -- "). - Second‑order: значение безопасно сохранено, но позже используется в небезопасном конкатенированном запросе. Техники предотвращения и как их применять (лучшие практики) 1) Параметризованные запросы / Prepared statements (рекомендуется всегда) - Java (JDBC): String sql = "SELECT * FROM users WHERE name = ?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, name); ResultSet rs = ps.executeQuery(); - PHP (PDO): $stmt = $pdo->prepare("SELECT * FROM users WHERE name = :name"); $stmt->execute(['name' => $name]); - MySQLi (PHP): $stmt = $mysqli->prepare("SELECT * FROM users WHERE name = ?"); $stmt->bind_param("s", $name); - Python (psycopg2/Postgres): cur.execute("SELECT * FROM users WHERE name = %s", (name,)) - C# (SqlServer): var cmd = new SqlCommand("SELECT * FROM users WHERE name = @name", conn); cmd.Parameters.AddWithValue("@name", name); Примечание: параметры передаются отдельно от SQL, драйвер правильно экранирует/биндит. 2) Правильное использование в разных СУБД - PostgreSQL: используйте параметризацию (libpq PQexecParams, psycopg2). Подготовленные планы безопасны. - MySQL: PreparedStatements и mysqli/PDO без эмуляции. В PDO отключите emulate_prepares (PDO::ATTR_EMULATE_PREPARES => false). - SQL Server: используйте параметризованные SqlCommand/StoredProcedures с SqlParameter. - Oracle: bind variables (JDBC, OCI8) — обязательны для безопасности и производительности. - SQLite: используйте привязку параметров (?, :name). 3) Хранимые процедуры — только если параметры используются корректно - Хранимые процедуры безопасны, если принимают параметры и не формируют динамический SQL конкатенацией. Если внутри процедуры делается динамический SQL, нужно использовать bind‑переменные там же. 4) Валидация и белые списки - Для имён/логинов/ID — разрешайте только ожидаемые символы и длину (регэксп). Белый список предпочтительнее черных списков. - Для чисел — приводите к числовому типу и проверяйте диапазон: e.g. Integer.parseInt, проверка диапазона. 5) Экранирование — как крайняя мера - Нативное экранирование (например, mysqli_real_escape_string) менее предпочтительно, может быть ошибочно применено; только если нельзя использовать параметры. Для PDO используйте quote осторожно. - Экранирование не заменяет принципа минимальных прав. 6) Минимизация прав и разделение ролей - Учётная запись БД для приложения должна иметь минимально необходимые привилегии (нет DROP/DDL, если не требуется). - Если нужен доступ к разным данным — используйте разные учетные записи. 7) Логи, WAF и мониторинг - Логируйте подозрительные входы, мониторьте аномалии. - WAF может блокировать известные паттерны, но не заменяет параметризацию. 8) Дополнительные меры - Избегайте динамической генерации SQL для идентификаторов (таблицы/столбцы). Если необходимо, тщательно валидация белым списком. - Обрабатывайте вторичные эффекты (second‑order), санитизируйте при использовании. Короткая сводка рекомендаций - Всегда используйте параметризованные запросы/prepared statements или привязку параметров. - Валидация по белому списку для входных данных. - Минимальные привилегии для аккаунта БД. - Экранирование — только как запасной вариант и с учётом СУБД‑специфики. - Проверяйте настройки драйвера (напр., отключить PDO emulate_prepares) и не разрешайте множественные запросы, если это ненужно. Если нужно — могу привести краткие рабочие примеры для конкретной СУБД/языка (JDBC, PDO, psycopg2, ADO.NET и т.д.).
- Фрагмент String q = "SELECT * FROM users WHERE name = '" + name + "'"; — формирует SQL путём конкатенации данных пользователя и кода: это классическая SQL‑инъекция. Любой символ из переменной name попадёт в запрос без проверки/экранирования.
Как атакующий может воспользоваться (примеры)
- Тавтология (вернёт все записи): если name = "' OR 1=11=11=1 -- " → запрос станет
SELECT * FROM users WHERE name = '' OR 1=11=11=1 -- '
(комментарий отсекает остаток).
- UNION‑инъекция (получить другие столбцы): name = "' UNION SELECT password FROM users -- "
→ позволяет слить результат другого SELECT.
- Закрытие строки + дополнительные команды (stacked queries): name = "'; DROP TABLE users; -- "
(работает, если драйвер/СУБД разрешают множественные запросы).
- Blind‑инъекции: побитовые/временные техники, если видно только true/false или задержку (например, name = "' OR (SELECT IF(SUBSTR(password,1,1)='a', SLEEP(5), 0)) -- ").
- Second‑order: значение безопасно сохранено, но позже используется в небезопасном конкатенированном запросе.
Техники предотвращения и как их применять (лучшие практики)
1) Параметризованные запросы / Prepared statements (рекомендуется всегда)
- Java (JDBC):
String sql = "SELECT * FROM users WHERE name = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, name);
ResultSet rs = ps.executeQuery();
- PHP (PDO):
$stmt = $pdo->prepare("SELECT * FROM users WHERE name = :name");
$stmt->execute(['name' => $name]);
- MySQLi (PHP):
$stmt = $mysqli->prepare("SELECT * FROM users WHERE name = ?");
$stmt->bind_param("s", $name);
- Python (psycopg2/Postgres):
cur.execute("SELECT * FROM users WHERE name = %s", (name,))
- C# (SqlServer):
var cmd = new SqlCommand("SELECT * FROM users WHERE name = @name", conn);
cmd.Parameters.AddWithValue("@name", name);
Примечание: параметры передаются отдельно от SQL, драйвер правильно экранирует/биндит.
2) Правильное использование в разных СУБД
- PostgreSQL: используйте параметризацию (libpq PQexecParams, psycopg2). Подготовленные планы безопасны.
- MySQL: PreparedStatements и mysqli/PDO без эмуляции. В PDO отключите emulate_prepares (PDO::ATTR_EMULATE_PREPARES => false).
- SQL Server: используйте параметризованные SqlCommand/StoredProcedures с SqlParameter.
- Oracle: bind variables (JDBC, OCI8) — обязательны для безопасности и производительности.
- SQLite: используйте привязку параметров (?, :name).
3) Хранимые процедуры — только если параметры используются корректно
- Хранимые процедуры безопасны, если принимают параметры и не формируют динамический SQL конкатенацией. Если внутри процедуры делается динамический SQL, нужно использовать bind‑переменные там же.
4) Валидация и белые списки
- Для имён/логинов/ID — разрешайте только ожидаемые символы и длину (регэксп). Белый список предпочтительнее черных списков.
- Для чисел — приводите к числовому типу и проверяйте диапазон: e.g. Integer.parseInt, проверка диапазона.
5) Экранирование — как крайняя мера
- Нативное экранирование (например, mysqli_real_escape_string) менее предпочтительно, может быть ошибочно применено; только если нельзя использовать параметры. Для PDO используйте quote осторожно.
- Экранирование не заменяет принципа минимальных прав.
6) Минимизация прав и разделение ролей
- Учётная запись БД для приложения должна иметь минимально необходимые привилегии (нет DROP/DDL, если не требуется).
- Если нужен доступ к разным данным — используйте разные учетные записи.
7) Логи, WAF и мониторинг
- Логируйте подозрительные входы, мониторьте аномалии.
- WAF может блокировать известные паттерны, но не заменяет параметризацию.
8) Дополнительные меры
- Избегайте динамической генерации SQL для идентификаторов (таблицы/столбцы). Если необходимо, тщательно валидация белым списком.
- Обрабатывайте вторичные эффекты (second‑order), санитизируйте при использовании.
Короткая сводка рекомендаций
- Всегда используйте параметризованные запросы/prepared statements или привязку параметров.
- Валидация по белому списку для входных данных.
- Минимальные привилегии для аккаунта БД.
- Экранирование — только как запасной вариант и с учётом СУБД‑специфики.
- Проверяйте настройки драйвера (напр., отключить PDO emulate_prepares) и не разрешайте множественные запросы, если это ненужно.
Если нужно — могу привести краткие рабочие примеры для конкретной СУБД/языка (JDBC, PDO, psycopg2, ADO.NET и т.д.).