SQL Injection: уязвимости и защита (prepared statements, параметризация)

Что такое SQL-инъекция и почему она опасна? 🔓

SQL-инъекция — это уязвимость, которая позволяет злоумышленнику вмешиваться в SQL-запросы вашего приложения. Представьте, что ваш код превращается в послушную марионетку в руках хакера!

Пример уязвимого кода на Python (с SQLite):

username = input("Введите логин: ")
password = input("Введите пароль: ")

# ⚠️ Уязвимый запрос! Никогда так не делайте!
query = f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}'"
cursor.execute(query)

Если ввести admin' -- как логин, запрос превратится в:

SELECT * FROM users WHERE username = 'admin' --' AND password = '...'

Комментарий -- отключает проверку пароля, и злоумышленник входит как админ!


Как работают Prepared Statements 🛡️

Prepared Statements (подготовленные выражения) — это самый надёжный способ защиты. Вместо вставки данных напрямую в запрос, мы используем параметры.

Исправленный безопасный пример:

username = input("Введите логин: ")
password = input("Введите пароль: ")

# ✅ Безопасный запрос с параметрами
query = "SELECT * FROM users WHERE username = ? AND password = ?"
cursor.execute(query, (username, password))

Почему это безопасно? База данных:

  1. Сначала компилирует шаблон запроса
  2. Затем подставляет значения как данные (не как часть кода)

Параметризация в разных СУБД 📊

Принцип везде одинаков, но синтаксис отличается:

MySQL (Python + mysql-connector):

query = "INSERT INTO products (name, price) VALUES (%s, %s)"
cursor.execute(query, ("Телевизор", 39900))

PostgreSQL (Psycopg2):

query = "UPDATE employees SET salary = %s WHERE id = %s"
cursor.execute(query, (75000, 42))

SQL Server (pyodbc):

query = "DELETE FROM orders WHERE id = ?"
cursor.execute(query, (1001,))

Что ещё важно знать о защите? 🔍

  1. Никогда не доверяйте пользовательскому вводу — даже данные из cookies или headers могут быть опасны.
  2. Используйте ORM с умом — Django ORM, SQLAlchemy автоматически экранируют запросы, но raw SQL требует осторожности.
  3. Ограничивайте права — у приложения должны быть только необходимые права в БД (не root!).

Пример опасного raw-запроса в Django:

# ⚠️ Уязвимость!
User.objects.raw(f"SELECT * FROM auth_user WHERE username = '{username}'")

Безопасная альтернатива:

User.objects.raw("SELECT * FROM auth_user WHERE username = %s", [username])

Практическая демонстрация атаки 💻

Давайте смоделируем взлом:

# Уязвимое приложение
def unsafe_login():
    username = input("Логин: ")
    password = input("Пароль: ")
    cursor.execute(f"SELECT * FROM users WHERE login='{username}' AND pass='{password}'")
    return cursor.fetchone()

# Вводим:
# Логин: admin' --
# Пароль: что угодно

Результат: Получаем доступ к учётной записи admin без пароля!


Главные выводы 🎯

  1. Всегда используйте параметризованные запросы
  2. Никогда не конкатенируйте SQL-строки вручную
  3. Проводите аудит безопасности своего кода
  4. Изучайте документацию вашей СУБД по теме защиты

Теперь вы вооружены знаниями, чтобы делать свои приложения неуязвимыми! 💪

Скрыть рекламу навсегда

🌱 Индвидидулаьные занятия

Индивидуальные онлайн-занятия по программированию для детей и подростков

Личный подход, без воды, с фокусом на понимание и реальные проекты.

🚀 Записаться на занятие