Что такое SQL-инъекция и как от неё защититься в Go?

Ответ

SQL-инъекция (SQL Injection) — это тип атаки на базу данных, при которой злоумышленник внедряет вредоносный SQL-код в запрос через входные данные, передаваемые приложением.

Это происходит, когда приложение напрямую конкатенирует (склеивает) строки для формирования SQL-запроса, не проверяя и не экранируя пользовательский ввод.

Пример уязвимого кода на Go:

// НЕ ДЕЛАЙТЕ ТАК!
userInput := "' OR 1=1 --"
query := fmt.Sprintf("SELECT * FROM users WHERE name = '%s'", userInput)

// Запрос превратится в:
// SELECT * FROM users WHERE name = '' OR 1=1 --'

// Условие `OR 1=1` всегда истинно, а `--` комментирует остаток запроса.
// В результате будут выбраны ВСЕ пользователи из таблицы.

rows, err := db.Query(query)

Как защититься: основной метод

Используйте параметризованные запросы (Prepared Statements).

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

Пример безопасного кода:

// Используем плейсхолдер `?` для MySQL или `$1` для PostgreSQL
query := "SELECT * FROM users WHERE username = ? AND password = ?"

username := "admin"
password := "some_password"

// Данные передаются как отдельные аргументы в Query() или Exec()
// Драйвер сам позаботится о безопасной подстановке
rows, err := db.QueryContext(ctx, query, username, password)

Другие методы защиты

  1. Использование ORM: Библиотеки вроде GORM или sqlx по умолчанию используют параметризованные запросы под капотом, что делает код более безопасным.
  2. Валидация и очистка ввода (Sanitization): Всегда проверяйте, что пользовательский ввод соответствует ожидаемому формату (например, что ID — это число, а email — валидный адрес). Это дополнительный слой защиты.
  3. Принцип наименьших привилегий: Пользователь БД, от имени которого приложение выполняет запросы, должен иметь только необходимые права (например, только SELECT, INSERT, UPDATE на конкретные таблицы, но не DROP).