← Назад к вопросам

Что такое инъекции SQL?

2.0 Middle🔥 121 комментариев
#Базы данных

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое SQL-инъекции?

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

Как работает SQL-инъекция?

Механизм основан на неправильном конструировании SQL-запросов путем конкатенации или интерполяции пользовательского ввода (данных из форм, параметров URL, cookie) непосредственно в строку запроса. Вместо того чтобы обрабатывать пользовательские данные как значения, приложение ошибочно интерпретирует их как часть команд SQL.

Рассмотрим классический пример на Go с использованием стандартной библиотеки database/sql:

// НЕПРАВИЛЬНЫЙ подход: уязвимый код
func unsafeLogin(db *sql.DB, username, password string) bool {
    query := fmt.Sprintf("SELECT * FROM users WHERE username='%s' AND password='%s'", username, password)
    rows, err := db.Query(query)
    // ... обработка результата
}

Если злоумышленник введёт в поле username значение admin' --, SQL-запрос примет вид:

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

Символы -- в большинстве СУБД обозначают комментарий, поэтому часть запроса после них игнорируется. В результате злоумышленник получает доступ от имени администратора без знания пароля.

Более опасные варианты инъекций позволяют выполнять несколько запросов, извлекать данные из других таблиц или даже удалять базу данных.

Типы SQL-инъекций

  • Классические (In-band): Результат атаки сразу отображается в ответе приложения.
    *   **На основе ошибок (Error-based)**: Злоумышленник вызывает ошибки СУБД, чтобы получить информацию о структуре базы данных.
    *   **На основе UNION (Union-based)**: Используется оператор SQL `UNION` для объединения результатов легитимного запроса с данными из других таблиц.
  • Слепые (Blind): Приложение не возвращает результаты запроса или ошибки напрямую, но злоумышленник может воссоздать информацию, анализируя поведение приложения (время ответа или изменение логики).
    *   **Boolean-based blind**: Анализируется разница в ответе приложения на истинные и ложные условия в инъекции.
    *   **Time-based blind**: Используются функции задержки (например, `SLEEP()` в MySQL, `pg_sleep()` в PostgreSQL) для определения истинности условия по времени ответа.
  • Инъекции второго порядка: Вредоносные данные сначала сохраняются в базе (например, при регистрации), а затем неправильно используются в другом, более критичном запросе (например, при смене пароля).

Защита от SQL-инъекций в Go

Go предоставляет мощные встроенные механизмы для безопасной работы с базами данных. Основной и абсолютно необходимый метод защиты — использование параметризованных запросов (подготовленных выражений).

// ПРАВИЛЬНЫЙ подход: использование подготовленных выражений
func safeLogin(db *sql.DB, username, password string) bool {
    query := "SELECT * FROM users WHERE username=? AND password=?" // Для MySQL/PostgreSQL
    // Для PostgreSQL также можно использовать: WHERE username=$1 AND password=$2
    rows, err := db.Query(query, username, password)
    // ... обработка результата
}

Почему это безопасно? Драйвер базы данных гарантированно разделяет код запроса и данные. Параметры (даже если они содержат SQL-код) всегда обрабатываются как значения, а не как часть командной строки запроса. Драйвер экранирует их в соответствии с требованиями СУБД.

Дополнительные меры безопасности в Go-приложениях

  1. Использование ORM или Query Builder: Библиотеки типа GORM, sqlx или squirrel по умолчанию используют параметризацию, снижая риск человеческой ошибки. Однако важно использовать их API правильно, а не прибегать к ручной интерполяции строк.
  2. Принцип минимальных привилегий: Учётная запись приложения в СУБД должна иметь только те права, которые необходимы для работы (чаще только SELECT, INSERT, UPDATE на конкретные таблицы, но не DROP, CREATE или доступ к системным таблицам).
  3. Валидация и санитизация ввода: Хотя это не заменяет параметризованные запросы, валидация на уровне приложения (например, проверка формата email, длины строки) помогает отсечь заведомо некорректные данные.
  4. Регулярное обновление зависимостей: Используйте go mod и следите за обновлениями драйверов БД (github.com/go-sql-driver/mysql, github.com/lib/pq), в которых могут быть исправлены уязвимости.
  5. Сканирование кода и тестирование на проникновение: Используйте статические анализаторы (например, gosec) для автоматического поиска шаблонов, похожих на инъекции. Проводите ручное или автоматизированное (с помощью инструментов вроде sqlmap) тестирование безопасности.

Пример уязвимости и исправления с использованием sqlx

// Уязвимый код (конкатенация)
func getOrdersVulnerable(db *sqlx.DB, userId string) ([]Order, error) {
    var orders []Order
    err := db.Select(&orders, "SELECT * FROM orders WHERE user_id=" + userId) // ОПАСНО!
    return orders, err
}

// Защищённый код (параметризованный запрос)
func getOrdersSafe(db *sqlx.DB, userId string) ([]Order, error) {
    var orders []Order
    query := "SELECT * FROM orders WHERE user_id=?"
    err := db.Select(&orders, query, userId) // БЕЗОПАСНО. sqlx использует подготовленные выражения.
    return orders, err
}

Вывод: SQL-инъекция — это критическая уязвимость, полностью предотвратимая на уровне разработки. В Go для этого достаточно строго придерживаться использования параметризованных запросов через стандартный интерфейс database/sql или современные библиотеки-обёртки. Никогда не следует доверять пользовательскому вводу и строить SQL-запросы путем конкатенации строк.