На примере SQL-запроса: "SELECT * FROM users WHERE name = '%s'" — опишите уязвимость, возможные последствия и как правильно строить безопасные запросы в разных СУБД
Уязвимость: SQL‑инъекция. - Что происходит: строка запроса собирается через подстановку: `query = "SELECT * FROM users WHERE name = '%s'" % name` — если `name` содержит управляющие символы/части SQL, они попадут в запрос как код. Примеры атаки: `name = "' OR '1'='1"` — вернёт все строки; `name = "'; DROP TABLE users; --"` — может удалить таблицу. - Последствия: утечка данных, обход аутентификации, модификация/удаление данных, получение привилегий, удалённое выполнение команд (в комбинации с уязвимостями СУБД/приложения). Как правильно строить безопасные запросы (общие принципы) - Никогда не подставляйте сырые строки из внешних данных в SQL. Используйте параметризованные запросы (prepared statements / bind parameters). - Применяйте allow‑list валидацию для ожидаемых форматов (например, числовые id). - Ограничивайте привилегии аккаунта БД, логируйте и мониторьте запросы. - Экранирование (escape) — как крайняя мера и только с надёжной библиотечной функцией СУБД. - Хранимые процедуры безопасны только если они принимают параметры, а не конкатенируют строки внутри. Конкретные примеры безопасных запросов - MySQL (Python, PyMySQL / MySQLdb): cursor.execute("SELECT * FROM users WHERE name = %s", (name,)) - PostgreSQL (psycopg2): cursor.execute("SELECT * FROM users WHERE name = %s", (name,)) - libpq (C) / прямой SQL с параметрами: SQL: "SELECT * FROM users WHERE name = $1" — передаёте параметр через PQexecParams / PQprepare+PQexecPrepared. - SQLite (Python): cursor.execute("SELECT * FROM users WHERE name = ?", (name,)) - SQL Server (C#, ADO.NET): var cmd = new SqlCommand("SELECT * FROM users WHERE name = @name", conn); cmd.Parameters.AddWithValue("@name", name); - Oracle (OCI / PDO_OCI): stmt = conn.prepare("SELECT * FROM users WHERE name = :name"); stmt.execute({":name": name}); - JDBC (Java): PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE name = ?"); ps.setString(1, name); ResultSet rs = ps.executeQuery(); - PHP PDO (любая СУБД через PDO): $stmt = $pdo->prepare("SELECT * FROM users WHERE name = :name"); $stmt->execute([':name' => $name]); ORM / query builders: - Используйте готовые ORM/QueryBuilder и их механизмы параметризации (например, SQLAlchemy, ActiveRecord). Они формируют безопасные параметры автоматически. Когда можно использовать экранирование (не предпочитается): - mysql_real_escape_string / mysqli_real_escape_string (MySQL), PQescapeLiteral (Postgres) — требует внимательной обработки кодировок и всё равно более рискованно, чем параметры. Дополнительно: - Не выдавайте подробные ошибки SQL пользователю. - Применяйте принцип наименьших привилегий и регулярный аудит привилегий/логов. Кратко: отказ от конкатенации строк и повсеместное использование параметризованных запросов (bind parameters) — основной и надёжный способ защиты от SQL‑инъекций.
- Что происходит: строка запроса собирается через подстановку:
`query = "SELECT * FROM users WHERE name = '%s'" % name` — если `name` содержит управляющие символы/части SQL, они попадут в запрос как код. Примеры атаки:
`name = "' OR '1'='1"` — вернёт все строки;
`name = "'; DROP TABLE users; --"` — может удалить таблицу.
- Последствия: утечка данных, обход аутентификации, модификация/удаление данных, получение привилегий, удалённое выполнение команд (в комбинации с уязвимостями СУБД/приложения).
Как правильно строить безопасные запросы (общие принципы)
- Никогда не подставляйте сырые строки из внешних данных в SQL. Используйте параметризованные запросы (prepared statements / bind parameters).
- Применяйте allow‑list валидацию для ожидаемых форматов (например, числовые id).
- Ограничивайте привилегии аккаунта БД, логируйте и мониторьте запросы.
- Экранирование (escape) — как крайняя мера и только с надёжной библиотечной функцией СУБД.
- Хранимые процедуры безопасны только если они принимают параметры, а не конкатенируют строки внутри.
Конкретные примеры безопасных запросов
- MySQL (Python, PyMySQL / MySQLdb):
cursor.execute("SELECT * FROM users WHERE name = %s", (name,))
- PostgreSQL (psycopg2):
cursor.execute("SELECT * FROM users WHERE name = %s", (name,))
- libpq (C) / прямой SQL с параметрами:
SQL: "SELECT * FROM users WHERE name = $1" — передаёте параметр через PQexecParams / PQprepare+PQexecPrepared.
- SQLite (Python):
cursor.execute("SELECT * FROM users WHERE name = ?", (name,))
- SQL Server (C#, ADO.NET):
var cmd = new SqlCommand("SELECT * FROM users WHERE name = @name", conn);
cmd.Parameters.AddWithValue("@name", name);
- Oracle (OCI / PDO_OCI):
stmt = conn.prepare("SELECT * FROM users WHERE name = :name");
stmt.execute({":name": name});
- JDBC (Java):
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE name = ?");
ps.setString(1, name);
ResultSet rs = ps.executeQuery();
- PHP PDO (любая СУБД через PDO):
$stmt = $pdo->prepare("SELECT * FROM users WHERE name = :name");
$stmt->execute([':name' => $name]);
ORM / query builders:
- Используйте готовые ORM/QueryBuilder и их механизмы параметризации (например, SQLAlchemy, ActiveRecord). Они формируют безопасные параметры автоматически.
Когда можно использовать экранирование (не предпочитается):
- mysql_real_escape_string / mysqli_real_escape_string (MySQL), PQescapeLiteral (Postgres) — требует внимательной обработки кодировок и всё равно более рискованно, чем параметры.
Дополнительно:
- Не выдавайте подробные ошибки SQL пользователю.
- Применяйте принцип наименьших привилегий и регулярный аудит привилегий/логов.
Кратко: отказ от конкатенации строк и повсеместное использование параметризованных запросов (bind parameters) — основной и надёжный способ защиты от SQL‑инъекций.