1. Сравнительный анализ методов защиты
Сравнительный анализ методов защиты от SQL-инъекций
На протяжении всего курса мы изучали различные методы защиты от SQL-инъекций: от prepared statements до network policies. В этом завершающем обзоре мы сведём все техники в единую картину и определим, когда и какой метод применять.
Уровни защиты: Defence in Depth
Надёжная защита от SQL-инъекций строится по принципу эшелонированной обороны (defence in depth). Ни один метод не является универсальным — комбинация техник на разных уровнях обеспечивает максимальную безопасность.
Уровень 1: Код приложения
| Метод | Защищает от | Ограничения | Когда применять |
|---|---|---|---|
| Prepared Statements | Все классические SQLi (UNION, Boolean, Time-based, Second-Order) | Не применимы к динамическим идентификаторам (ORDER BY, имена таблиц) | Всегда — это основа защиты |
| ORM (GORM, sqlx) | Классические SQLi через автоматическую параметризацию | Raw-запросы в ORM остаются уязвимыми | Для стандартных CRUD-операций |
| Whitelist валидация | ORDER BY injection, динамические идентификаторы | Требует ручного обновления при изменении схемы | Для динамических колонок, сортировки, фильтров |
| Типизация ввода | Инъекции в числовые параметры | Не помогает для строковых полей | Для ID, limit, offset |
// Prepared Statements — основа защиты
row := db.QueryRowContext(ctx, "SELECT * FROM users WHERE id = ?", userID)
// Whitelist — для динамических идентификаторов
allowedColumns := map[string]bool{"name": true, "email": true, "created_at": true}
if !allowedColumns[sortColumn] {
sortColumn = "created_at" // безопасное значение по умолчанию
}
query := fmt.Sprintf("SELECT * FROM users ORDER BY %s", sortColumn)
// Типизация — для числовых параметров
id, err := strconv.Atoi(rawID)
if err != nil {
http.Error(w, "Invalid ID", http.StatusBadRequest)
return
}
Уровень 2: База данных
| Метод | Защищает от | Ограничения | Когда применять |
|---|---|---|---|
| Принцип наименьших привилегий | Эскалацию последствий (DROP, INSERT в системные таблицы) | Не предотвращает саму инъекцию | Всегда — при настройке БД |
| Отдельные пользователи для read/write | Утечку данных через write-only контексты | Сложнее управление миграциями | Для production-систем |
| Ограничение доступа к системным таблицам | Enumeration через information_schema | Ограничивает возможности администрирования | Для production |
-- Создание пользователя только для чтения
CREATE USER app_reader WITH PASSWORD 'secure_password';
GRANT SELECT ON users, products, orders TO app_reader;
-- Создание пользователя для записи
CREATE USER app_writer WITH PASSWORD 'another_password';
GRANT SELECT, INSERT, UPDATE ON users, products, orders TO app_writer;
-- НЕ давать DELETE, DROP, ALTER
Уровень 3: Инфраструктура
| Метод | Защищает от | Ограничения | Когда применять |
|---|---|---|---|
| WAF (Web Application Firewall) | Известные паттерны атак | Обход через кодировки, JSON, нестандартный синтаксис | Как дополнительный слой |
| Network Policies (Egress) | Out-of-Band exfiltration | Не предотвращает in-band атаки | Для K8s / облачных сред |
| Таймауты (context.WithTimeout) | Time-based blind SQLi, DoS через тяжёлые запросы | Не предотвращает быстрые инъекции | Всегда — для всех запросов к БД |
| Rate Limiting | Автоматизированные атаки (sqlmap) | Не помогает против ручной эксплуатации | Для публичных эндпоинтов |
// Таймаут запроса — обязательная практика
ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second)
defer cancel()
row := db.QueryRowContext(ctx, "SELECT * FROM users WHERE email = ?", email)
Уровень 4: Мониторинг и детекция
| Метод | Защищает от | Ограничения | Когда применять |
|---|---|---|---|
| Логирование аномальных запросов | Помогает обнаружить атаку постфактум | Не предотвращает атаку | Всегда |
| Alerting на SQL-ошибки | Раннее обнаружение попыток инъекции | Ложные срабатывания | Для production |
| Slow query log | Обнаружение time-based атак | Шум от легитимных тяжёлых запросов | Для production |
Матрица: Тип атаки vs Метод защиты
| Тип атаки | Prepared Statements | Whitelist | Типизация | WAF | Таймауты | Network Policies |
|---|---|---|---|---|---|---|
| Classic (UNION) | + | - | - | +/- | - | - |
| Boolean-based Blind | + | - | - | +/- | - | - |
| Time-based Blind | + | - | - | +/- | + | - |
| Second-Order | + | - | - | - | - | - |
| ORDER BY injection | - | + | - | +/- | - | - |
| Out-of-Band (OOB) | + | - | - | - | - | + |
| WAF Bypass | + | + | + | - | - | - |
| Automated (sqlmap) | + | + | + | +/- | - | - |
Легенда: + = эффективно, - = не помогает, +/- = частично помогает
Рекомендуемый минимум для Go-проекта
Каждый Go-проект, работающий с базой данных, должен включать как минимум:
// 1. Prepared Statements для ВСЕХ запросов
db.QueryContext(ctx, "SELECT ... WHERE col = ?", param)
// 2. Таймауты на каждый запрос
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
// 3. Whitelist для динамических частей
func safeSortColumn(input string) string {
allowed := map[string]string{
"name": "name",
"date": "created_at",
"price": "price",
}
if col, ok := allowed[input]; ok {
return col
}
return "created_at"
}
// 4. Типизация числовых параметров
func safeIntParam(raw string, defaultVal int) int {
val, err := strconv.Atoi(raw)
if err != nil || val < 0 {
return defaultVal
}
return val
}
Антипаттерны: что НЕ делать
- Не полагаться только на WAF — он обходится (см. Модуль 9)
- Не использовать чёрные списки (blacklisting) — всегда найдётся способ обхода
- Не экранировать вручную (
strings.ReplaceAll(input, "'", "''")) — это ненадёжно - Не доверять данным из БД — Second-Order SQLi использует именно это доверие
- Не игнорировать ORM raw-запросы —
db.Raw("SELECT * FROM users WHERE name = '" + name + "'")так же уязвим
Заключение
Защита от SQL-инъекций — это не один инструмент, а система мер на всех уровнях: от написания безопасного кода до настройки инфраструктуры и мониторинга. Prepared statements являются фундаментом, но без дополнительных слоёв (whitelist, таймауты, network policies, логирование) защита остаётся неполной.
Продолжить чтение
Что бы прочитать модуль полностью, зарегистрируйтесь/войдите на платформу
Когда закончишь — отметь раздел, чтобы продолжить.