Может ли в SQL-запросе быть инъекция и как ее предотвратить
Что проверяют
Понимаете ли вы, почему параметризация защищает, а экранирование и валидация нет. В Go интервьюеру особенно важно, где в вашем коде остаются места для склейки строк даже при использовании ORM. Следом часто просят: «как безопасно сделать сортировку по полю, которое выбрал пользователь?»
Как отвечать
Да, инъекция возникает всякий раз, когда пользовательский ввод становится частью текста SQL. Классика: "... WHERE name = '" + name + "'", и строка ' OR 1=1 -- меняет смысл запроса.
Защита в Go строится так:
- плейсхолдеры в
database/sql:db.QueryContext(ctx, "SELECT id FROM users WHERE name = $1", name). Значение уходит отдельно от текста запроса и не разбирается как SQL. Синтаксис зависит от драйвера:$1у pgx иlib/pq,?у MySQL. Отдельныйdb.Prepareдля этого не нужен,Queryс аргументами уже параметризован; - идентификаторы через белый список: имя таблицы, колонку в
ORDER BYи направление сортировки параметром не передать. Заведитеmap[string]stringдопустимых значений и подставляйте только из неё; - списки: вместо склейки
IN (...)в PostgreSQL передают срез в= ANY($1), в sqlx для этого естьsqlx.In; - ORM и генераторы: GORM безопасен в форме
db.Where("name = ?", name), ноdb.Where(fmt.Sprintf(...))иdb.Order(userInput)пропускают сырой SQL. sqlc генерирует типизированные функции из готовых запросов, и места для склейки там просто нет.
Дополнительные слои: отдельный пользователь базы с минимальными правами, чтобы инъекция не дошла до DROP, и линтер gosec, чьи правила G201 и G202 ругаются на сборку SQL через fmt.Sprintf и конкатенацию.
Где спотыкаются
Называют главной защитой экранирование или валидацию ввода. Это вспомогательные меры, а ручное экранирование рано или поздно ошибается на кодировке или редком символе.
Считают, что ORM закрывает вопрос целиком. Первый же Order или Raw с пользовательской строкой возвращает уязвимость.
Пытаются передать имя колонки плейсхолдером и удивляются, что сортировка не работает. Значение подставляется как константа, а не как имя колонки, и порядок строк от него не зависит.