> Как защититься от SQL-инъекций (Go)

Уровень: senior · Роль: backend · Категория: Технические вопросы

Компании: InDrive, VK

Стек: Go

> Пример ответа

Короткий ответ

Основной способ защиты от SQL-инъекций в Go - использование параметризованных запросов через database/sql с плейсхолдерами (? для MySQL/PostgreSQL, $1 для PostgreSQL). Никогда не конкатенируйте пользовательский ввод в строку SQL. Дополнительно: валидация входных данных, принцип наименьших привилегий для БД-пользователя, использование ORM с параметризацией по умолчанию. Экранирование - не защита, а лишь заплатка.

Подробное объяснение

SQL-инъекция возникает, когда злоумышленник встраивает SQL-код в пользовательский ввод, который затем интерполируется в запрос. В Go уязвимость появляется при использовании fmt.Sprintf или конкатенации строк для построения запроса.

Параметризованные запросы работают так: вы отправляете в БД шаблон запроса с плейсхолдерами и отдельно - значения. Драйвер и СУБД обрабатывают значения как данные, а не как код. Это устраняет возможность инъекции на уровне протокола.

Важно понимать: параметризация защищает от инъекций в WHERE, INSERT, UPDATE, но не работает для идентификаторов (имён таблиц, колонок) и ORDER BY. Для таких случаев нужен strict whitelist допустимых значений.

Дополнительные меры:

  • валидация типов и форматов на уровне приложения;
  • ограничение прав пользователя БД (например, запрет DROP, DELETE без необходимости);
  • использование prepared statements для повторяющихся запросов (улучшает производительность и безопасность);
  • логирование запросов без чувствительных данных.

На практике

В Go стандартный подход - db.Query, db.Exec, db.QueryRow с плейсхолдерами. Для PostgreSQL используйте $1, $2; для MySQL - ?. При работе с динамическими условиями (фильтры) стройте запрос с фиксированным числом плейсхолдеров и добавляйте значения в args.

Для идентификаторов применяйте whitelist: map[string]string{"id": "id", "name": "name"} и проверяйте ввод по ключам. Для ORDER BY - аналогично, с проверкой направления (ASC/DESC) через явное сравнение.

При использовании ORM (GORM, sqlx) убедитесь, что вы не используете методы, допускающие сырой SQL с интерполяцией (например, Where("name = ?", input) - безопасно, а Where(fmt.Sprintf("name = '%s'", input)) - нет).

Пример кода

GO
// Безопасно: параметризованный запрос
func GetUserByEmail(db *sql.DB, email string) (*User, error) {
var u User
err := db.QueryRow(
"SELECT id, email, name FROM users WHERE email = $1",
email,
).Scan(&u.ID, &u.Email, &u.Name)
if err != nil {
return nil, err
}
return &u, nil
}
// Опасно: конкатенация
func UnsafeGetUser(db *sql.DB, email string) (*User, error) {
query := "SELECT id, email, name FROM users WHERE email = '" + email + "'"
// email = "'; DROP TABLE users; --"
// приводит к выполнению произвольного SQL
row := db.QueryRow(query)
// ...
}
// Безопасно для динамического ORDER BY
func GetUsersSorted(db *sql.DB, sortBy string, asc bool) ([]User, error) {
allowedSorts := map[string]string{
"id": "id",
"name": "name",
"email": "email",
}
col, ok := allowedSorts[sortBy]
if !ok {
col = "id"
}
direction := "DESC"
if asc {
direction = "ASC"
}
// col и direction - из whitelist, не из пользовательского ввода
rows, err := db.Query("SELECT id, email, name FROM users ORDER BY " + col + " " + direction)
// ...
}

Как отвечать на собеседовании

Начните с главного: параметризованные запросы - основной механизм защиты. Затем упомяните ограничения (идентификаторы, ORDER BY) и дополнительные меры. Приведите пример уязвимого и безопасного кода. Покажите понимание, что экранирование - не решение. Если спросят про ORM - объясните разницу между безопасными и опасными методами. Упомяните про prepared statements на стороне БД.

Что проверяет интервьюер

  • понимание принципа работы параметризации (данные vs код);
  • знание синтаксиса плейсхолдеров для разных СУБД;
  • умение видеть уязвимость в коде с конкатенацией;
  • знание edge cases: динамические идентификаторы, ORDER BY, LIKE;
  • осведомлённость о дополнительных слоях защиты (валидация, права, whitelist);
  • способность объяснить, почему экранирование недостаточно.

Типичные ошибки

  • утверждение, что экранирование кавычек решает проблему;
  • использование fmt.Sprintf для значений в запросе;
  • игнорирование проблемы с ORDER BY и именами колонок;
  • предположение, что ORM полностью защищает от инъекций;
  • отсутствие упоминания про ограничение прав БД;
  • путаница между плейсхолдерами разных СУБД (? vs $1).

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью