Дан кусок SQL: query = "SELECT * FROM users WHERE name = '" + name + "'"; — объясните уязвимость, возможные последствия и конкретные способы исправления для разных СУБД
Уязвимость - Код строит SQL через конкатенацию: query = "SELECT * FROM users WHERE name = '" + name + "'"; Это — классическая SQL‑инъекция: attacker управляет переменной namenamename и может изменить структуру запроса. Пример эксплуатации - Если name=name = name=′′OR′1′=′1′'' OR '1'='1'′′OR′1′=′1′ результат запроса становится всегда истинным и вернёт все записи. - Более вредоносно: name=name = name=′′;DROPTABLEusers;−−''; DROP TABLE users; --′′;DROPTABLEusers;−− — может удалить таблицу (если СУБД и контекст позволяют). Возможные последствия - Чтение конфиденциальных данных (утечка пользователей, паролей, кредитных карт). - Модификация/удаление данных (INSERT/UPDATE/DELETE, DROP). - Проброс команд, выполнение нескольких запросов (в СУБД, поддерживающих многоинструкционный SQL). - Аутентификация/эскалация прав (bypass логина). - Диверсия на ОС (в определённых конфигурациях, через расширения) и RCE. - Длительные простои, компрометация целостности и соответствия. Конкретные способы исправления (по СУБД / платформам) Общее правило: не конкатенировать строки — использовать параметризованные запросы (prepared statements / bind parameters), проверку и минимальные привилегии. 1) MySQL (PHP, Java, Python и др.) - PHP PDO (рекомендуется): - $stmt = $pdo->prepare("SELECT * FROM users WHERE name = :name"); $stmt->execute(['name' => $name]); - mysqli (если нельзя PDO): использовать подготовленные выражения или как минимум mysqli_real_escape_string (крайний случай). - Java JDBC: - PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE name = ?"); ps.setString(1, name); - Python (mysqlclient / PyMySQL / mysql-connector): - cursor.execute("SELECT * FROM users WHERE name = %s", (name,)) 2) PostgreSQL - psycopg2: - cur.execute("SELECT * FROM users WHERE name = %s", (name, )) - Java JDBC — тоже PreparedStatement. - Никогда не подставлять параметры через текст запроса; для динамических идентификаторов (имя таблицы/столбца) — использовать жёсткий белый список. 3) Microsoft SQL Server - ADO.NET: - using (var cmd = new SqlCommand("SELECT * FROM users WHERE name = @name", conn)) { cmd.Parameters.AddWithValue("@name", name); ... } - Для динамического SQL — использовать sp_executesql с параметрами: - EXEC sp_executesql N'SELECT * FROM users WHERE name = @name', N'@name nvarchar(100)', @name = @p0; - Не разрешать прямую конкатенацию строк для SQL. 4) Oracle - cx_Oracle (Python) / OCI / JDBC: - cursor.execute("SELECT * FROM users WHERE name = :name", {"name": name}) - Использовать привязки (bind variables). 5) SQLite - Python sqlite3: - cur.execute("SELECT * FROM users WHERE name = ?", (name,)) - Для других языков — аналогичные bind placeholders. Дополнительные рекомендации (все СУБД) - Валидировать вход: для ожидаемых форматов (email, id) — применять белые списки/регулярные выражения. Для идентификаторов (имя таблицы/столбца) — разрешать только заранее определённые значения. - Принцип наименьших привилегий: аккаунт БД для приложения не должен иметь прав DROP/ALTER/ADMIN, если это не нужно. - Логирование и мониторинг подозрительных запросов. - Использовать ORM/Query builders — но проверять, что они корректно параметризуют запросы. - Если невозможно параметризовать (напр. динамические имена столбцов), тщательно валидировать/включать в белый список. - Экранирование (escape) — только как временная мера; bind‑параметры предпочтительнее. Краткое резюме - Уязвимость: SQL‑инъекция из‑за конкатенации строк. - Исправление: всегда использовать параметризованные запросы / bind variables; дополнительно — валидация, белые списки для идентификаторов, минимальные привилегии и мониторинг.
- Код строит SQL через конкатенацию:
query = "SELECT * FROM users WHERE name = '" + name + "'";
Это — классическая SQL‑инъекция: attacker управляет переменной namenamename и может изменить структуру запроса.
Пример эксплуатации
- Если name=name = name= ′′OR′1′=′1′'' OR '1'='1'′′OR′1′=′1′ результат запроса становится всегда истинным и вернёт все записи.
- Более вредоносно: name=name = name= ′′;DROPTABLEusers;−−''; DROP TABLE users; --′′;DROPTABLEusers;−− — может удалить таблицу (если СУБД и контекст позволяют).
Возможные последствия
- Чтение конфиденциальных данных (утечка пользователей, паролей, кредитных карт).
- Модификация/удаление данных (INSERT/UPDATE/DELETE, DROP).
- Проброс команд, выполнение нескольких запросов (в СУБД, поддерживающих многоинструкционный SQL).
- Аутентификация/эскалация прав (bypass логина).
- Диверсия на ОС (в определённых конфигурациях, через расширения) и RCE.
- Длительные простои, компрометация целостности и соответствия.
Конкретные способы исправления (по СУБД / платформам)
Общее правило: не конкатенировать строки — использовать параметризованные запросы (prepared statements / bind parameters), проверку и минимальные привилегии.
1) MySQL (PHP, Java, Python и др.)
- PHP PDO (рекомендуется):
- $stmt = $pdo->prepare("SELECT * FROM users WHERE name = :name");
$stmt->execute(['name' => $name]);
- mysqli (если нельзя PDO): использовать подготовленные выражения или как минимум mysqli_real_escape_string (крайний случай).
- Java JDBC:
- PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE name = ?");
ps.setString(1, name);
- Python (mysqlclient / PyMySQL / mysql-connector):
- cursor.execute("SELECT * FROM users WHERE name = %s", (name,))
2) PostgreSQL
- psycopg2:
- cur.execute("SELECT * FROM users WHERE name = %s", (name, ))
- Java JDBC — тоже PreparedStatement.
- Никогда не подставлять параметры через текст запроса; для динамических идентификаторов (имя таблицы/столбца) — использовать жёсткий белый список.
3) Microsoft SQL Server
- ADO.NET:
- using (var cmd = new SqlCommand("SELECT * FROM users WHERE name = @name", conn)) { cmd.Parameters.AddWithValue("@name", name); ... }
- Для динамического SQL — использовать sp_executesql с параметрами:
- EXEC sp_executesql N'SELECT * FROM users WHERE name = @name', N'@name nvarchar(100)', @name = @p0;
- Не разрешать прямую конкатенацию строк для SQL.
4) Oracle
- cx_Oracle (Python) / OCI / JDBC:
- cursor.execute("SELECT * FROM users WHERE name = :name", {"name": name})
- Использовать привязки (bind variables).
5) SQLite
- Python sqlite3:
- cur.execute("SELECT * FROM users WHERE name = ?", (name,))
- Для других языков — аналогичные bind placeholders.
Дополнительные рекомендации (все СУБД)
- Валидировать вход: для ожидаемых форматов (email, id) — применять белые списки/регулярные выражения. Для идентификаторов (имя таблицы/столбца) — разрешать только заранее определённые значения.
- Принцип наименьших привилегий: аккаунт БД для приложения не должен иметь прав DROP/ALTER/ADMIN, если это не нужно.
- Логирование и мониторинг подозрительных запросов.
- Использовать ORM/Query builders — но проверять, что они корректно параметризуют запросы.
- Если невозможно параметризовать (напр. динамические имена столбцов), тщательно валидировать/включать в белый список.
- Экранирование (escape) — только как временная мера; bind‑параметры предпочтительнее.
Краткое резюме
- Уязвимость: SQL‑инъекция из‑за конкатенации строк.
- Исправление: всегда использовать параметризованные запросы / bind variables; дополнительно — валидация, белые списки для идентификаторов, минимальные привилегии и мониторинг.