> Как защититься от 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 Usererr := 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; --"// приводит к выполнению произвольного SQLrow := db.QueryRow(query)// ...}// Безопасно для динамического ORDER BYfunc 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).
> Похожие задачи по backend
Где используется recover в Go?
Какое взаимодействие между сервисами предпочтительнее: синхронное или асинхронное
Как сделать так, чтобы изменения слайса в функции были видны в вызывающей функции
Являются ли мапы в Go потокобезопасными
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью