SQL-инъекции: почему возникают и как закрыть
SQL-инъекция — одна из самых старых и опасных уязвимостей. Разберёмся, откуда она берётся и как её закрыть навсегда.
Этичный взгляд: мы изучаем, чтобы строить защиту. Тестировать инъекции можно только на своих или специально разрешённых учебных приложениях.
Что это такое
SQL-инъекция возникает, когда данные от пользователя попадают прямо в текст SQL-запроса. База данных не отличает «команду программиста» от «данных пользователя», если их склеить в одну строку. В результате ввод пользователя может изменить смысл запроса.
Корень проблемы — склейка строк
Представим, что разработчик строит запрос так (это небезопасный пример, показывающий принцип):
query = "SELECT * FROM users WHERE name = '" + user_input + "'"Если пользователь введёт обычное имя, всё работает. Но если ввод содержит кавычку и служебные символы SQL, структура запроса ломается, и условие может стать всегда истинным. Проблема не в SQL и не в пользователе, а в том, что данные смешали с кодом.
Защита: параметризованные запросы
Правильное решение — никогда не склеивать пользовательский ввод с текстом запроса. Вместо этого используют параметры (placeholders). База получает шаблон запроса и отдельно — значения. Значение всегда трактуется как данные, а не как команда.
import sqlite3
con = sqlite3.connect(":memory:")
cur = con.cursor()
cur.execute("CREATE TABLE users(name TEXT, role TEXT)")
cur.execute("INSERT INTO users VALUES('alice','admin')")
cur.execute("INSERT INTO users VALUES('bob','user')")
# Безопасно: ? — это placeholder, значение передаётся отдельно
user_input = "alice"
cur.execute("SELECT role FROM users WHERE name = ?", (user_input,))
print("Роль:", cur.fetchone()[0])
# Даже «опасный» ввод теперь просто не найдёт совпадения
bad = "alice' OR '1'='1"
cur.execute("SELECT role FROM users WHERE name = ?", (bad,))
print("Опасный ввод нашёл записей:", len(cur.fetchall()))Запустите код: «опасный» ввод во втором случае не сломает логику — он воспринимается как обычная строка-имя, которой просто нет в таблице.
Слои защиты
- Параметризация — главный приём, закрывает большинство случаев.
- ORM (например, Django ORM, SQLAlchemy) — по умолчанию используют параметры.
- Принцип наименьших привилегий — у пользователя БД ровно те права, что нужны приложению.
- Валидация ввода — дополнительный, но не основной слой.
Запомните формулу: данные и код нельзя смешивать. Это правило работает не только для SQL, но и для других инъекций.