Если вы хотите получить реальную защиту на периметре веб-приложения и при этом сохранить контроль и предсказуемость, связка Nginx + ModSecurity 3 (libmodsecurity) + OWASP Core Rule Set — проверенный временем выбор. В этой статье соберу практические шаги для админов и девопсов: как аккуратно внедрить WAF через динамический модуль Nginx, включить OWASP CRS, запустить в режиме обнаружения, отловить и погасить false positives, а затем перевести в блокирующий режим. Параллельно разберём тонкости производительности, лимиты тела запроса, исключения (exclusions) и аудит-лог.
Что такое ModSecurity 3 и OWASP CRS в контексте Nginx
ModSecurity 3 — это библиотека libmodsecurity и коннектор-модуль к Nginx. В отличие от старых сборок ModSecurity 2, здесь Nginx взаимодействует с libmodsecurity через отдельный dynamic module. Правила берём из OWASP Core Rule Set (OWASP CRS) — открытого набора сигнатур и эвристик для классических инъекций, XSS, RCE, протокольных аномалий и многого другого.
OWASP CRS работает в режиме anomaly scoring: вместо немедленной блокировки каждое подозрительное совпадение начисляет «штрафные баллы»; при превышении порога запрос блокируется. Это снижает ложные срабатывания и делает настройку управляемой.
Подготовка окружения и варианты установки
Идеальный для продакшна подход — поставить libmodsecurity3 и OWASP CRS из репозитория дистрибутива, а коннектор к Nginx собрать отдельно как динамический модуль с флагом совместимости --with-compat. Важно, чтобы модуль собирался ровно на тех исходниках Nginx, которые установлены в системе.
Пакеты, которые понадобятся
libmodsecurity3(при необходимостиlibmodsecurity-dev).- Правила OWASP CRS (пакет
modsecurity-crsили аналог для вашего дистрибутива). - Сборочная среда: компилятор, зависимости и исходники Nginx той же версии.
Подсказка: для Debian/Ubuntu удобно использовать
apt source nginx, чтобы гарантировать совпадение версии исходников и собранного бинаря.
Пример для Debian/Ubuntu: сборка динамического модуля
sudo apt update
sudo apt install -y libmodsecurity3 libmodsecurity-dev modsecurity-crs build-essential git devscripts
apt source nginx
cd nginx-*
git clone --depth 1 https://github.com/SpiderLabs/ModSecurity-nginx ../ModSecurity-nginx
./configure --with-compat --add-dynamic-module=../ModSecurity-nginx
make modules
sudo mkdir -p /usr/lib/nginx/modules
sudo cp objs/ngx_http_modsecurity_module.so /usr/lib/nginx/modules/
Далее подключим модуль в конфиге Nginx и настроим ModSecurity с OWASP CRS.
Подключение динамического модуля в Nginx
Добавьте строку load_module в верхний уровень конфигурации Nginx (обычно /etc/nginx/nginx.conf или файл в /etc/nginx/modules-enabled/):
load_module /usr/lib/nginx/modules/ngx_http_modsecurity_module.so;
Проверяем синтаксис и версии:
nginx -t
nginx -V
Базовая конфигурация ModSecurity + OWASP CRS
Создадим каталог для локальной конфигурации и аудит-логов:
sudo mkdir -p /etc/nginx/modsec
sudo mkdir -p /var/log/nginx/modsec
sudo chown -R www-data:www-data /var/log/nginx/modsec
Скопируем базовый конфиг ModSecurity и включим движок:
sudo cp /etc/modsecurity/modsecurity.conf-recommended /etc/nginx/modsec/modsecurity.conf
# В /etc/nginx/modsec/modsecurity.conf установите:
# SecRuleEngine On
Подготовим отдельный main.conf для подключения ModSecurity и CRS из Nginx:
sudo tee /etc/nginx/modsec/main.conf > /dev/null << 'EOF'
# Подключаем основной конфиг ModSecurity
Include /etc/nginx/modsec/modsecurity.conf
# Режим обнаружения для безопасного старта
SecRuleEngine DetectionOnly
# Аудит-лог
SecAuditEngine RelevantOnly
SecAuditLog /var/log/nginx/modsec/audit.log
SecAuditLogType Serial
SecAuditLogParts ABIJDEFHZ
# Лимиты тела запроса (согласуйте с Nginx)
SecRequestBodyLimit 13107200
SecRequestBodyNoFilesLimit 131072
SecRequestBodyInMemoryLimit 1048576
# Включаем OWASP CRS (пути зависят от дистрибутива)
Include /usr/share/modsecurity-crs/crs-setup.conf
Include /usr/share/modsecurity-crs/rules/*.conf
EOF
Подключим ModSecurity в Nginx, но только для динамики — статике WAF не нужен:
http {
server {
listen 80;
server_name example.com;
root /var/www/app;
# Статика: WAF выключен
location ~* \.(?:css|js|png|jpg|jpeg|gif|svg|ico|woff2?)$ {
access_log off;
expires 7d;
add_header Cache-Control "public";
}
# API и динамика: WAF включён
location / {
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/main.conf;
proxy_pass http://127.0.0.1:9000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}
Проверяем конфигурацию и перезапускаем Nginx:
nginx -t
sudo systemctl reload nginx

Запуск в режиме обнаружения и контроль ложных срабатываний
Начинать стоит с SecRuleEngine DetectionOnly. В этом режиме запросы не блокируются, но ModSecurity и OWASP CRS записывают срабатывания в аудит-лог. Пронаблюдайте хотя бы несколько рабочих дней, соберите список повторяющихся ложных срабатываний и только после этого переходите к блокировкам.
Где смотреть логи:
/var/log/nginx/error.log— краткие заметки о блокировках и ошибках./var/log/nginx/modsec/audit.log— подробный аудит (ID правил, фазы, целевые параметры).
Совет: добавьте в access_log корреляционный идентификатор запроса и прокидывайте его в upstream — это ускорит разбор инцидентов и поиск по аудит-логу.
http {
log_format main '$remote_addr - $request_id [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"';
access_log /var/log/nginx/access.log main;
map $request_id $x_req_id { default $request_id; }
add_header X-Request-ID $x_req_id;
}
Подстройка OWASP CRS: Paranoia Level, пороги и профили
Откройте crs-setup.conf и задайте параметры под свой трафик. Ключевые пункты:
- Paranoia Level (
tx.paranoia_level): 1 — щадящий, 4 — строгий. Для старта берите 1. - Пороги блокировки (
tx.inbound_anomaly_score_threshold,tx.outbound_anomaly_score_threshold): дефолт 5 — хорошая база. - Разрешённые методы (
tx.allowed_methods) и типы контента (tx.allowed_request_content_type): сузьте под ваш API.
# Вариант настройки через SecAction одной строкой
SecAction "id:900110,phase:1,pass,nolog,setvar:tx.paranoia_level=1,setvar:tx.inbound_anomaly_score_threshold=5,setvar:tx.outbound_anomaly_score_threshold=4,setvar:tx.allowed_methods=GET HEAD POST PUT PATCH DELETE,setvar:tx.allowed_request_content_type=application/json|application/x-www-form-urlencoded|multipart/form-data"
Повышайте paranoia level и/или снижайте пороги, только если поток ложных срабатываний контролируем. Если false positives много — начните с исключений (ниже), а затем корректируйте уровень.
Исключения: точечные, безопасные, воспроизводимые
Главная ошибка внедрения WAF — массовые отключения правил. Вместо этого применяйте точечные исключения по параметрам, путям или типам контента. В Nginx это можно делать как в общих файлах правил, так и локально в server/location через modsecurity_rules.
Удаление цели у конкретного правила
Допустим, поле search легитимно содержит SQL-операторы, и беспокоит правило из семейства 9421xx (SQLi). Уберём это поле из целей конкретного правила:
modsecurity on;
modsecurity_rules '
SecRuleEngine On
SecRuleUpdateTargetById 942100 "!ARGS:search"
';
modsecurity_rules_file /etc/nginx/modsec/main.conf;
Отключение правила на маршруте
Нужен «коридор безопасности» для конкретного endpoint? Локально отключите правило или группу ID:
location = /webhook/incoming {
modsecurity on;
modsecurity_rules '
SecRuleEngine On
SecRuleRemoveById 932100 932105
';
modsecurity_rules_file /etc/nginx/modsec/main.conf;
proxy_pass http://127.0.0.1:9000;
}
Практические замечания:
- Перед исключением убедитесь, что оно действительно нужно: проверьте соответствующие записи в
audit.log. - Фиксируйте номер задачи в комментариях — пригодится при аудитах.
- Регулярно пересматривайте список исключений и убирайте устаревшие.
Лимиты тела запроса: согласование Nginx и ModSecurity
Загрузки файлов и крупные JSON часто «ломаются» из‑за несовпадения лимитов. Синхронизируйте client_max_body_size в Nginx и SecRequestBodyLimit в ModSecurity.
server {
client_max_body_size 12m;
location /upload {
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/main.conf;
proxy_request_buffering on;
proxy_pass http://127.0.0.1:9000;
}
}
Если client_max_body_size меньше — клиент получит 413 от Nginx; если меньше SecRequestBodyLimit — ModSecurity может вернуть 403 и запись REQBODY_PROCESSOR_ERROR в аудит-лог. Проверяйте на реальных нагрузках.

Перевод в блокирующий режим
Когда поток ложных срабатываний под контролем и исключения согласованы, меняем SecRuleEngine DetectionOnly на SecRuleEngine On в /etc/nginx/modsec/main.conf, перезагружаем Nginx и продолжаем мониторинг.
- Включайте блокировку поэтапно: с одного сервера или части маршрутов.
- Оценивайте метрики отказов, error budget и корреляцию с релизами приложения.
- Постепенно расширяйте покрытие.
Производительность и зона применения WAF
ModSecurity 3 добавляет задержку на обработку запроса (обычно миллисекунды; зависит от набора правил и тела). Чтобы не «жечь» ресурсы:
- Не включайте WAF для статики и медиа.
- Исключайте health-check-и и служебные локации.
- Для крупных загрузок включайте буферизацию запросов (
proxy_request_buffering on) и синхронизируйте лимиты. - Подбирайте разумный paranoia level и пороги; избегайте избыточных кастомных правил.
Планируйте ресурсы. На нагруженных API выносите проверяемые маршруты в отдельные серверные блоки или инстансы. Если нужна изоляция и гибкое масштабирование, разверните WAF на отдельном VDS.
Логирование и аудит: что включить
Для продуктивного расследования инцидентов обеспечьте:
- Access-log с
$request_idи ключевыми полями клиента. - Audit-log ModSecurity с частями
ABIJDEFHZ— достаточно контекста без избыточности. - Error-log Nginx уровня
infoилиnoticeна этапе отладки.
Практики для audit-log:
- Храните логи отдельно, включите ротацию и убедитесь, что она не ломает дескрипторы (совместите с перезагрузкой Nginx).
- Рассмотрите JSON-формат — удобно для парсинга и передачи в SIEM/лог-агрегатор.
Тестирование правил: быстро проверить срабатывание
Чтобы убедиться, что WAF работает, отправьте очевидный «атакующий» паттерн, например строку, похожую на SQLi или XSS:
curl -i 'http://example.com/?q=1%20UNION%20SELECT%201,2,3'
В аудит-логе вы увидите срабатывание правил семейства 9421xx. В режиме обнаружения будет запись, а в блокирующем — ответ с кодом 403.
Типичные ошибки и их диагностика
- unknown directive "modsecurity": модуль не загружен или неверный путь в
load_module. - Версия Nginx не совпадает: модуль собран не с теми исходниками — пересоберите с
apt source nginx. - Аудит-лог пуст: проверьте
SecAuditEngine, права на каталог логов и ротацию. - Много 403 на загрузках: проверьте
client_max_body_size,SecRequestBodyLimitиproxy_request_buffering. - False positives на JSON: сузьте
tx.allowed_request_content_typeи добавьте точечныеSecRuleUpdateTargetByIdдля «шумных» полей.
Стратегия внедрения: от «детекта» к «блокировке» без аварий
- Включите WAF на ключевых динамических маршрутах в режиме DetectionOnly.
- Соберите статистику по срабатываниям и «шумным» параметрам.
- Внедрите точечные исключения и профиль CRS (paranoia, пороги, методы, типы контента).
- Включите блокировку на ограниченном сегменте и оцените отказоустойчивость.
- Расширяйте покрытие, обновляйте правила, проводите «гигиену» исключений.
Поддержка и обновления
Регулярно обновляйте:
- libmodsecurity3 — исправления стабильности и безопасности;
- OWASP CRS — новые правила и уточнения паттернов;
- коннектор-модуль — при обновлении Nginx пересобирайте модуль на соответствующих исходниках.
Перед обновлением правил включайте «детект-режим» на тестовом стенде или канареечном сервере, чтобы поймать возможные новые false positives до выката на весь прод. И не забывайте про HTTPS: корректная TLS-терминация и актуальные SSL-сертификаты — обязательная база для безопасного периметра.
Вывод
Связка Nginx + ModSecurity 3 + OWASP CRS — зрелый WAF, который можно внедрить без боли, если идти по «лестнице зрелости»: динамический модуль с совместимостью, корректное подключение CRS, старт в режиме обнаружения, точечные исключения и постоянный мониторинг. Двигайтесь итерациями, фиксируйте эффекты на метриках — и WAF станет надёжным и предсказуемым слоем защиты вашего веб-приложения.


