ModSecurity — это модуль веб-сервера, который проверяет каждый HTTP-запрос и ответ по набору правил и умеет блокировать атаку раньше, чем она дойдёт до приложения. Сам по себе модуль лишь исполнитель: содержание в него вкладывает набор правил OWASP Core Rule Set (CRS), где собраны сигнатуры SQL-инъекций, XSS, path traversal, внедрения команд и другой типовой активности. Ниже — рабочая связка ModSecurity 3 и CRS для nginx на Debian или Ubuntu: установка, проверка, настройка исключений и честный разговор об ограничениях.
Что WAF даёт и чего не даёт
WAF закрывает класс атак, которые трудно отловить на уровне приложения: массовые сканеры, известные уязвимости в зависимостях, грубые попытки инъекций. Он не заменяет исправленный код, серверную валидацию и обновления библиотек. Практическая польза появляется, когда защита стоит перед конкретным сайтом и её журнал кто-то регулярно читает.
- фильтрация запросов и ответов по сигнатурам CRS;
- журнал срабатываний с идентификаторами правил;
- режим наблюдения, когда блокировка ещё не включена;
- точечные исключения для легитимных форм и API.
Установка ModSecurity для nginx
В Debian и Ubuntu модуль есть в готовом пакете, сборка из исходников не требуется. Заодно проверьте, что nginx видит динамический модуль и конфигурация остаётся валидной.
sudo apt update
sudo apt install nginx libnginx-mod-http-modsecurity
ls /etc/nginx/modules-enabled/
nginx -t
Пакет добавляет файл подключения в modules-enabled, а сам модуль лежит в /usr/lib/nginx/modules. Для Apache логика похожая: пакет libapache2-mod-security2 и команда a2enmod security2. Дальше примеры для nginx, потому что он чаще стоит перед приложением.
Подключаем OWASP CRS
Правила удобно держать в /etc/nginx/modsec. Клонируйте репозиторий и создайте рабочий конфиг из шаблона.
sudo mkdir -p /etc/nginx/modsec
sudo git clone https://github.com/coreruleset/coreruleset.git /etc/nginx/modsec/coreruleset
cd /etc/nginx/modsec/coreruleset
sudo cp crs-setup.conf.example crs-setup.conf
sudo install -d -o www-data -g www-data /var/lib/modsecurity
sudo install -o www-data -g adm -m 640 /dev/null /var/log/nginx/modsec_audit.log
sudo touch /etc/nginx/modsec/local-exclusions.conf
Теперь опишите базовые параметры движка. Файл modsecurity.conf создаём сами, чтобы не зависеть от того, какие образцы конфигов положил дистрибутив.
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecRequestBodyLimit 13107200
SecResponseBodyAccess On
SecResponseBodyMimeType text/plain text/html application/json
SecResponseBodyLimit 1048576
SecAuditEngine RelevantOnly
SecAuditLogRelevantStatus "^(?:5|4(?!04))"
SecAuditLogParts ABIJDEFHZ
SecAuditLog /var/log/nginx/modsec_audit.log
SecTmpDir /var/lib/modsecurity
SecDataDir /var/lib/modsecurity
Осталось связать всё в main.conf. Порядок включений важен: сначала настройки движка, затем crs-setup.conf, затем сами правила и локальные исключения.
Include /etc/nginx/modsec/modsecurity.conf
Include /etc/nginx/modsec/coreruleset/crs-setup.conf
Include /etc/nginx/modsec/coreruleset/rules/*.conf
Include /etc/nginx/modsec/local-exclusions.conf
В блоке server включите модуль и укажите файл правил:
server {
listen 443 ssl;
server_name example.ru;
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/main.conf;
}
Файл local-exclusions.conf уже создан пустым, он понадобится позже. Проверка конфигурации и перезагрузка:
sudo nginx -t && sudo systemctl reload nginx
Проверка в режиме DetectionOnly
Первый запуск всегда делайте без блокировки. Убедитесь, что обычный запрос проходит, а тестовая инъекция фиксируется в журнале.
curl -s -o /dev/null -w '%{http_code}\n' 'https://example.ru/?q=hello'
curl -s -o /dev/null -w '%{http_code}\n' 'https://example.ru/?id=1%27%20OR%20%271%27%3D%271'
sudo tail -n 30 /var/log/nginx/modsec_audit.log
В режиме DetectionOnly оба запроса вернут 200, но во втором в журнале появится запись с id правила, например 942100 для SQL-инъекции. Если журнал пуст, проверьте права на каталоги и убедитесь, что модуль действительно загружен.
Включаем блокировку и настраиваем уровень
После недели наблюдения можно переключить движок в боевой режим. Не начинайте сразу с максимальной паранойи: CRS имеет уровни с первого по четвёртый, и на высоких уровнях заметно растёт число ложных срабатываний.
sudo sed -i 's/^SecRuleEngine .*/SecRuleEngine On/' /etc/nginx/modsec/modsecurity.conf
sudo nginx -t && sudo systemctl reload nginx
curl -s -o /dev/null -w '%{http_code}\n' 'https://example.ru/?id=1%27%20OR%20%271%27%3D%271'
Теперь ожидается 403. Уровень задаётся правилом 900000 в crs-setup.conf, менять значение нужно именно там, до загрузки остальных правил. Начните с первого уровня и повышайте только после разбора журналов.
Исключения без отключения защиты
Легитимная форма может содержать текст, похожий на атаку. Не отключайте правило целиком — уберите из проверки конкретный параметр. Исключения складывайте в local-exclusions.conf, который подключается после CRS.
# не проверять поле message правилом 942100
SecRuleUpdateTargetById 942100 "!ARGS:message"
# отключить правило для конкретного пути API
SecRule REQUEST_URI "@beginsWith /api/v1/feedback" \
"id:1001,phase:1,pass,nolog,ctl:ruleRemoveTargetById=942100;ARGS:message"
После правки снова nginx -t и reload. Каждое исключение фиксируйте комментарием с датой и причиной, иначе через полгода никто не вспомнит, зачем оно нужно.
Диагностика и производительность
Сводка по срабатываниям помогает найти самые шумные правила:
sudo grep -o 'id "[0-9]*"' /var/log/nginx/modsec_audit.log | sort | uniq -c | sort -rn | head
sudo grep -c 'id "949110"' /var/log/nginx/modsec_audit.log
Правило 949110 означает превышение порога аномалий на входящем трафике. Проверка тел ответов и больших файлов стоит процессорного времени, поэтому статику и скачивания лучше вывести из-под анализа:
SecRule REQUEST_URI "@rx ^/(static|media|download)/" \
"id:1000,phase:1,pass,nolog,ctl:ruleEngine=Off"
На слабой машине включайте SecResponseBodyAccess только для API с JSON. Замеряйте задержку до и после: разница в единицы миллисекунд нормальна, рост в разы — повод пересмотреть правила.
Отдельно настройте ротацию журнала: при активном сайте файл растёт быстро, а разбор гигабайтного лога превращается в отдельную задачу. В logrotate укажите daily, rotate 14 и postrotate с reload nginx, чтобы дескриптор переоткрылся корректно. Для отладки включайте SecDebugLog только на время: подробный вывод заметно замедляет обработку запросов.
Перед боевым режимом прогоните по сайту реалистичный сценарий: вход, восстановление пароля, загрузку файла, отправку длинного текста и работу админки. Ложные срабатывания дешевле найти на стенде, чем получать звонки от пользователей после включения блокировки.
Чек-лист перед боевым режимом
- неделя логов в DetectionOnly и разбор всех срабатываний;
- исключения оформлены отдельным файлом с комментариями;
- настроена ротация modsec_audit.log через logrotate;
- проверены формы входа, загрузка файлов и админка;
- есть тест, подтверждающий блокировку и пропуск обычного запроса.
Итог
ModSecurity с OWASP CRS — быстрый способ добавить сайту слой защиты от типовых атак без переписывания кода. Начните с DetectionOnly, соберите статистику, настройте точечные исключения и только потом включайте блокировку. Помните, что ModSecurity 3 развивается неспешно, а CRS требует регулярного обновления правил; альтернативой стоит держать в поле зрения Coraza. WAF дополняет, но не отменяет обновления зависимостей и нормальную валидацию входных данных.