Разберитесь в SQL-кейсе: "SELECT * FROM users WHERE name = '" + userInput + "';" — какие уязвимости здесь присутствуют, как их эксплуатируют и какие безопасные альтернативы предложить
Коротко и по делу — что здесь не так, как эксплуатируют и как исправить. 1) Уязвимости - SQL‑инъекция: конкатенация входа пользователя в SQL позволяет выполнить произвольный SQL. - Вторичные (second‑order) инъекции: введённые данные сохраняютcя и позже используются в небезопасных запросах. - Утечки информации через ошибки или ошибки времени выполнения (error‑/time‑based). - Возможные последствия: чтение/модификация/удаление данных, обход аутентификации, привилегированное выполнение, удалённое выполнение команд (зависит от СУБД и настроек). 2) Как эксплуатируют — примеры полезных полезных типов атак - Простая логическая инъекция (вернёт все строки): пример входа: "' OR 1=11=11=1 --" результат запроса: SELECT * FROM users WHERE name = '' OR 1=11=11=1 --'; - Stacked queries (если СУБД поддерживает): вход: "'; DROP TABLE users; --" — удалит таблицу. - UNION‑based (чтение других таблиц): вводит " ' UNION SELECT password FROM admin -- ". - Blind/Time‑based (когда нет видимого вывода): вводы вида " ' AND (SELECT IF(SUBSTRING(password,1,1)='a', SLEEP(5), 0)) -- " для по‑байтового извлечения. - Обход фильтров: кодирование (URL/Unicode), комментарии, подстановки, изменение регистра и т.д. - Вторичные: безопасно выглядящие записи позже используются в уязвимом динамическом SQL. 3) Безопасные альтернативы (приоритеты) - Подготовленные выражения / параметризованные запросы (самый надёжный метод). Примеры: - Java (JDBC): PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE name = ?"); ps.setString(1, userInput); - Python (psycopg2): cur.execute("SELECT * FROM users WHERE name = %s", (userInput,)) - PHP (PDO): $stmt = $pdo->prepare("SELECT * FROM users WHERE name = ?"); $stmt->execute([$userInput]); - Node.js (pg): client.query("SELECT * FROM users WHERE name = $1", [userInput]); - ORM/Query‑builder, которые используют параметризацию (SQLAlchemy, Entity Framework и т.д.). - Хранимые процедуры с параметрами (только если параметры не конкатенируются внутри процедуры). - Белый список/валидация входа (когда возможен строгий набор допустимых значений). - Экранирование/санитизация специфичная для СУБД — только как запасной вариант, если параметризация невозможна. - Минимальные привилегии для БД‑пользователей: запрет DDL/DCL для приложений, доступ только к нужным таблицам/операциям. - Логирование, мониторинг, ограничение длины ввода, WAF (как дополнительный слой, но не заменяет параметризацию). - Не показывать подробные SQL‑ошибки пользователю. 4) Короткие рекомендации по внедрению - Во всех местах, где используются внешние данные в SQL — обязать использование параметризованных запросов. - Рефакторить старый код, заменяя конкатенацию на prepared statements. - Тестировать (фаззинг, SQLi сканеры) и проводить ревью кода. - Применять принцип наименьших привилегий и резервное копирование. Вывод: проблема — SQL‑инъекция; решение — параметризованные запросы/подготовленные выражения + дополнительные меры (валидация, права, мониторинг).
1) Уязвимости
- SQL‑инъекция: конкатенация входа пользователя в SQL позволяет выполнить произвольный SQL.
- Вторичные (second‑order) инъекции: введённые данные сохраняютcя и позже используются в небезопасных запросах.
- Утечки информации через ошибки или ошибки времени выполнения (error‑/time‑based).
- Возможные последствия: чтение/модификация/удаление данных, обход аутентификации, привилегированное выполнение, удалённое выполнение команд (зависит от СУБД и настроек).
2) Как эксплуатируют — примеры полезных полезных типов атак
- Простая логическая инъекция (вернёт все строки):
пример входа: "' OR 1=11=11=1 --"
результат запроса: SELECT * FROM users WHERE name = '' OR 1=11=11=1 --';
- Stacked queries (если СУБД поддерживает): вход: "'; DROP TABLE users; --" — удалит таблицу.
- UNION‑based (чтение других таблиц): вводит " ' UNION SELECT password FROM admin -- ".
- Blind/Time‑based (когда нет видимого вывода): вводы вида " ' AND (SELECT IF(SUBSTRING(password,1,1)='a', SLEEP(5), 0)) -- " для по‑байтового извлечения.
- Обход фильтров: кодирование (URL/Unicode), комментарии, подстановки, изменение регистра и т.д.
- Вторичные: безопасно выглядящие записи позже используются в уязвимом динамическом SQL.
3) Безопасные альтернативы (приоритеты)
- Подготовленные выражения / параметризованные запросы (самый надёжный метод).
Примеры:
- Java (JDBC):
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE name = ?");
ps.setString(1, userInput);
- Python (psycopg2):
cur.execute("SELECT * FROM users WHERE name = %s", (userInput,))
- PHP (PDO):
$stmt = $pdo->prepare("SELECT * FROM users WHERE name = ?");
$stmt->execute([$userInput]);
- Node.js (pg): client.query("SELECT * FROM users WHERE name = $1", [userInput]);
- ORM/Query‑builder, которые используют параметризацию (SQLAlchemy, Entity Framework и т.д.).
- Хранимые процедуры с параметрами (только если параметры не конкатенируются внутри процедуры).
- Белый список/валидация входа (когда возможен строгий набор допустимых значений).
- Экранирование/санитизация специфичная для СУБД — только как запасной вариант, если параметризация невозможна.
- Минимальные привилегии для БД‑пользователей: запрет DDL/DCL для приложений, доступ только к нужным таблицам/операциям.
- Логирование, мониторинг, ограничение длины ввода, WAF (как дополнительный слой, но не заменяет параметризацию).
- Не показывать подробные SQL‑ошибки пользователю.
4) Короткие рекомендации по внедрению
- Во всех местах, где используются внешние данные в SQL — обязать использование параметризованных запросов.
- Рефакторить старый код, заменяя конкатенацию на prepared statements.
- Тестировать (фаззинг, SQLi сканеры) и проводить ревью кода.
- Применять принцип наименьших привилегий и резервное копирование.
Вывод: проблема — SQL‑инъекция; решение — параметризованные запросы/подготовленные выражения + дополнительные меры (валидация, права, мониторинг).