Как обнаружить атаки SQL-инъекций в логах веб-сервера
В прошлой статье мы говорили про анализ логов как ежедневную привычку. Сегодня я покажу, как эту привычку превратить в конкретный инструмент для охоты за SQL-инъекциями.
Я покажу четыре уровня защиты — от простого grep до машинного обучения. Вы сможете выбрать то, что подходит именно под ваш проект.
Теория: что мы ищем в логах
Прежде чем писать скрипты, давайте поймём, как SQL-инъекция выглядит в запросе к серверу.
Злоумышленник пытается подменить параметр запроса так, чтобы он стал частью SQL-команды. Классика:
sql
SELECT * FROM users WHERE username = 'admin' OR '1'='1' -- и пароль не нужен
В логе веб-сервера это будет выглядеть как-то так:
192.168.1.100 - - [20/Nov/2025:08:42:15 +0300] "GET /login?user=admin'+OR+'1'%3D'1'&pass=123 HTTP/1.1" 200 5324
Обратите внимание на URL-кодирование: %3D — это знак равенства, ' — апостроф. Наша задача — распознать такие паттерны .
Уровень 1: Ручной grep (экспресс-диагностика)
Самый быстрый способ понять, атакуют ли вас прямо сейчас — пройтись по логам классическим grep"ом.
Вот набор выражений, которые я держу под рукой:
```bash
Базовые SQL-инъекции (OR, UNION, SELECT)
grep -iE "(\%27)|(--)|(\%23)|(select.from)|(union.select)" /var/log/nginx/access.log
Попытки обхода авторизации
grep -iE "(\%27\%20OR\%20)|(\'\%20OR\%20)|('\%7C\%7C')" /var/log/nginx/access.log
Запросы к системным таблицам
grep -iE "(information_schema|mysql.|pg_|sqlite_)" /var/log/nginx/access.log
Функции для эксплуатации
grep -iE "(xp_cmdshell|exec master|exec xp|pg_sleep|waitfor delay|benchmark)" /var/log/nginx/access.log ```
Важно: Это только для первичного анализа. На проде такое лучше не гонять — тяжело и глазами читать сотни строк — то ещё удовольствие.
Уровень 2: Python-скрипт с регулярками
Для регулярного анализа я использую небольшие Python-утилиты. Они парсят лог построчно и бьют тревогу при совпадении с паттернами .
Вот упрощённая версия того, что реально работает у меня:
```python
!/usr/bin/env python3
import re import sys from collections import defaultdict from datetime import datetime
class SQLiDetector: def init(self, threshold=5): self.threshold = threshold self.suspicious_ips = defaultdict(int)
# Регулярки для разных СУБД
self.patterns = [
# MySQL/MariaDB
(r"(?i)(union.*select.*from|select.*from.*where.*=.*'|information_schema\.tables)", "MySQL"),
# PostgreSQL
(r"(?i)(pg_sleep|cast\(.*as.*\)|pg_|::text|::int)", "PostgreSQL"),
# MSSQL
(r"(?i)(xp_cmdshell|exec master|waitfor\s+delay|sp_executesql)", "MSSQL"),
# Oracle
(r"(?i)(dbms_pipe|utl_http|utl_file|ctxsys\.)", "Oracle"),
# Универсальные
(r"(\%27|\'|\-\-|%23|#)", "SQL метасимволы"),
(r"(?i)(or\s+1=1|or\s+true|or\s+\d+=\d+|and\s+1=1)", "Логические операторы"),
(r"(?i)(order\s+by\s+\d+|group\s+by\s+\d+|having\s+\d+=\d+)", "Перебор колонок"),
]
def analyze_line(self, line):
"""Анализ одной строки лога"""
# Типичный формат: IP - - [date] "method url" status size
match = re.match(r'(\d+\.\d+\.\d+\.\d+).*?"(?:GET|POST)\s+(\S+).*?"\s+(\d+)', line)
if not match:
return None
ip, url, status = match.groups()
# Декодируем URL
import urllib.parse
url = urllib.parse.unquote(url)
# Проверяем паттерны
for pattern, db_type in self.patterns:
if re.search(pattern, url):
self.suspicious_ips[ip] += 1
return {
'timestamp': datetime.now().isoformat(),
'ip': ip,
'url': url,
'status': status,
'pattern': db_type,
'matched': pattern
}
return None
def main(): detector = SQLiDetector()
# Читаем лог из stdin или файла
if len(sys.argv) > 1:
f = open(sys.argv[1], 'r')
else:
f = sys.stdin
alerts = []
for line in f:
result = detector.analyze_line(line)
if result:
alerts.append(result)
print(f"[ALERT] {result['timestamp']} - {result['ip']} пытается {result['pattern']}: {result['url'][:100]}")
# Если IP превысил порог — бьём тревогу громче
if detector.suspicious_ips[result['ip']] > detector.threshold:
print(f"*** ВНИМАНИЕ! IP {result['ip']} превысил порог подозрительной активности!")
if len(sys.argv) > 1:
f.close()
print(f"\nИтого: {len(alerts)} подозрительных запросов")
for ip, count in detector.suspicious_ips.items():
if count > 0:
print(f"{ip}: {count}")
if name == "main": main() ```
Этот скрипт не только ищет паттерны, но и считает количество подозрительных запросов с одного IP. Если их больше порога — пора бить в колокола .
Запускать можно так:
bash
cat /var/log/nginx/access.log | python3 sqli_detector.py
Уровень 3: Elasticsearch и готовые правила
Если у вас уже настроен ELK-стек, изобретать велосипед не надо. Elastic Security предлагает готовые правила для обнаружения SQL-инъекций .
Вот как выглядит реальное правило от Elastic (EQL-запрос):
eql
any where url.original like~ (
"*%20order%20by%*",
"*waitfor%20delay%20*",
"*%28select%20*from%20pg_sleep%285*",
"*union*select*",
"*or*1=1*",
"*into%20outfile*",
"*load_file%28*",
"*@@version*",
"*information_schema.tables*"
)
Правило мониторит индексы logs-nginx.access-*, logs-apache.access-*, logs-iis.access-* и срабатывает при совпадении с любым из паттернов .
Уровень 4: Машинное обучение (для тех, кому мало)
Регулярки хороши, но у них есть проблема: они ловят только то, что мы явно описали. Новый, неизвестный вариант атаки может пройти незамеченным.
Здесь на помощь приходит машинное обучение.
Что говорит наука
Недавнее исследование на Nginx-логах (1.65 миллиона записей) показало впечатляющие результаты :
| Алгоритм | Точность | F1-score | AUC | |----------|----------|----------|-----| | Random Forest | 99.92% | 99.94% | 0.9994 | | SVM | 96.45% | — | — |
Другое исследование подтверждает: Random Forest даёт точность 97.58% при обнаружении SQL-инъекций в сетевых логах .
Как это внедрить
Полноценный ML-пайплайн — задача нетривиальная. Но если хочется попробовать, вот roadmap:
- Сбор данных. Нужен датасет с размеченными логами (нормальные запросы и атаки).
- Извлечение признаков. Превращаем строки запросов в числа:
- Длина запроса
- Количество специальных символов
- Наличие ключевых слов
- Энтропия строки
- Обучение модели. Random Forest отлично работает "из коробки".
- Интеграция. Модель должна получать поток логов и классифицировать их в реальном времени.
В KNIME (платформа для data science) есть готовые компоненты для такого пайплайна .
Интеграция: от логов к алертам
Обнаружить атаку — полдела. Нужно ещё правильно на неё среагировать.
Вот как выглядит типовой процесс :
- Сбор. Логи стекаются в центральное хранилище (ELK, Tencent Cloud CLS, Aliyun).
- Анализ. Применяются правила (регулярки или ML).
- Обогащение. К IP подтягивается информация из threat intelligence (известные плохие адреса, геолокация).
- Корреляция. Событие связывается с другими активностями (например, был ли до этого брутфорс).
- Алерт. Если всё сходится — уведомление в Telegram, Slack или по почте.
- Блокировка. Можно автоматически добавить IP в firewall или WAF.
Практические рекомендации
На основе своего опыта и анализа источников , вот что я советую:
1. Включите детальное логирование
В MySQL:
sql
SET GLOBAL general_log = 'ON';
SET GLOBAL log_output = 'TABLE';
Но осторожно: на продакшене это может убить производительность. Лучше использовать специализированные инструменты вроде Database Audit .
2. Используйте WAF
Хороший WAF (Cloudflare, AWS WAF, Tencent Cloud WAF) отсечёт большинство примитивных атак ещё на подступах. В логах останется только то, что реально заслуживает внимания .
3. Смотрите не только на запросы, но и на ответы
Иногда атаку видно не по запросу, а по ответу сервера: - Неожиданно большие объёмы данных - Необычные статусы (200 на запрос с явной инъекцией) - SQL-ошибки в теле ответа
4. Автоматизируйте рутину
Человек не должен сидеть и глазами просматривать миллионы строк. Настройте систему так, чтобы она присылала только подозрительные события, а не весь лог.
Чек-лист для внедрения
Если вы хотите системно подойти к вопросу, вот что нужно сделать:
- [ ] Настроить централизованный сбор логов (ELK, Graylog, облачный сервис)
- [ ] Добавить базовые правила обнаружения (из этой статьи или готовые из Elastic)
- [ ] Настроить алерты в рабочий канал
- [ ] Раз в неделю просматривать статистику и подкручивать правила под свой проект
- [ ] Для критичных систем — рассмотреть внедрение ML-детекции или коммерческого SIEM
Вместо заключения
Обнаружение SQL-инъекций по логам — это как игра в детектива. Сначала вы ищете очевидные улики (OR 1=1), потом учитесь замечать мелкие несоответствия, а со временем начинаете предсказывать, где и когда преступник нанесёт следующий удар.
Начинайте с малого. Поставьте простой Python-скрипт, который раз в день прогоняет логи и шлёт отчёт в Telegram. Когда привыкнете к этому уровню осознанности, захочется большего — тогда и перейдёте на ELK с машинным обучением.
Главное — не останавливаться на достигнутом. Атаки становятся сложнее, но и наши инструменты не стоят на месте.