6. Защита: ORM vs Raw SQL
Защита: ORM vs Raw SQL
Один из самых частых вопросов Go-разработчика: «Обезопасит ли меня использование ORM?». Ответ прост: ORM помогает, но не является панацеей. Мы разберем, почему популярные библиотеки вроде GORM считаются «безопасными по дизайну» и как неосторожный вызов может свести всю эту защиту на нет.
Сравним разные подходы к работе с БД и выясним, где затаились ловушки.
1. Механика: "Автоматическая безопасность"
В чистом Go (database/sql) вы ДОЛЖНЫ помнить про плейсхолдеры ? или $1. Если вы забудете — вы уязвимы.
В ORM, таких как GORM, вы работаете с объектами:
// ✅ ПРАВИЛЬНЫЙ Go-код (GORM)
var user User
db.Where("username = ?", username).First(&user)
GORM по умолчанию использует Prepared Statements под капотом. Вам не нужно об этом думать.
2. Ловушка: Сырые запросы в ORM
Но даже в самых лучших ORM есть способ выстрелить себе в ногу. Это функции "сырого" SQL или неверное использование фильтров.
// ❌ СМЕРТЕЛЬНО ОПАСНО в GORM
db.Where(fmt.Sprintf("username = '%s'", username)).First(&user)
Если вы внутри ORM используете fmt.Sprintf — вы ПОВТОРЯЕТЕ всё те же ошибки, о которых мы говорили раньше. ORM не магическая стена, это просто помощник.
3. Таблица сравнения: Raw SQL vs sqlx vs GORM
| Характеристика | Чистый Go (Raw SQL) | sqlx | GORM (ORM) |
|---|---|---|---|
| Безопасность | 🌕 Нужно бдеть самому | 🌖 Помогает с именами | 🌕 По умолчанию безопасно |
| Гибкость | 🟢 Максимальная | 🟢 Высокая | 🟡 Ограниченная |
| Риск | 🔴 Очень высокий (Sprintf) | 🟡 Средний (Raw SQL) | 🟢 Низкий (если не Raw) |
4. Почему GORM иногда подводит?
Иногда разработчики пишут сложные JOIN или сортировки, используя ввод пользователя напрямую в db.Order(userInput). Именно здесь рождаются SQL-инъекции в современных Go-приложениях.
5. sqlx: именованные параметры как защита от ошибок
В чистом database/sql с тремя–четырьмя ?-плейсхолдерами легко перепутать порядок аргументов. Библиотека github.com/jmoiron/sqlx — это тонкая надстройка над stdlib, которая добавляет именованные параметры (:param) и автоматический маппинг строк в структуру.
import "github.com/jmoiron/sqlx"
// Один раз оборачиваем *sql.DB → *sqlx.DB
dbx := sqlx.NewDb(db, "sqlite3")
// Именованный параметр :search вместо позиционного ?
query := "SELECT id, name, description, price FROM products WHERE name LIKE :search"
rows, err := dbx.NamedQuery(query, map[string]interface{}{
"search": "%" + userInput + "%",
})
if err != nil { /* ... */ }
defer rows.Close()
for rows.Next() {
var p Product
if err := rows.StructScan(&p); err == nil { /* ... */ }
}
Почему это безопасно: под капотом NamedQuery использует ту же параметризацию, что и database/sql — значение :search передаётся отдельно от шаблона. Даже если ввод содержит ' UNION SELECT ..., база воспримет это как обычную строку для LIKE-сравнения. Бонус: при четырёх и более параметрах именованная форма читается лучше позиционной, а ошибка «перепутал аргументы» становится практически невозможной.
6. Сравнение Fix A (stdlib) vs Fix B (sqlx) для UNION-защиты
| Критерий | Fix A: db.Query("... LIKE ?", "%"+v+"%") |
Fix B: sqlx.NamedQuery(":search") |
|---|---|---|
| Зависимости | Только stdlib | github.com/jmoiron/sqlx |
| Читаемость при 3+ параметрах | Средняя (позиционные ?) |
Высокая (именованные :param) |
| Маппинг в структуру | Ручной Scan(&p.ID, &p.Name, ...) |
Автоматический StructScan(&p) |
| Производительность | Максимальная | Минимальный оверхед |
| Защита от SQLi | Полная (parametrized) | Полная (parametrized) |
| Когда выбрать | Простые запросы, минимум зависимостей | Проекты с большим количеством SQL |
Оба подхода эквивалентны по уровню безопасности — драйвер всё равно отправляет в БД отдельный pre-compiled шаблон. Выбор только между «ничего лишнего в зависимостях» и «удобство при росте кодовой базы».
ORM — не панацея, но мощный союзник. Если вы понимаете, как они работают, и не пытаетесь их обмануть сырыми запросами, вы существенно снижаете риск взлома своего приложения.
Вы изучили теорию UNION-атак и готовы к практике. Проверьте свои знания в квизе, а затем приступайте к лабораторной работе по извлечению данных из базы.
Продолжить чтение
Что бы прочитать модуль полностью, зарегистрируйтесь/войдите на платформу
Когда закончишь — отметь раздел, чтобы продолжить.