ModSecurity із набором правил OWASP CRS вмикають за десять хвилин і вимикають за три дні — після того, як статті перестають зберігатися в адмінці, ламається завантаження файлів, а клієнт не може оформити замовлення, бо в його адресі був апостроф. Висновок «WAF заважає працювати» напрошується сам, і він хибний: майже всі ці блокування лікуються трьома-чотирма точними винятками, і вся хитрість — знайти їх правильно.

Не вмикайте блокування одразу

Перший тиждень — лише спостереження. У /etc/modsecurity/modsecurity.conf:

SecRuleEngine DetectionOnly

У цьому режимі WAF записує все, що заблокував би, і не блокує нічого. Тиждень реального трафіку — разом із вашою роботою в адмінці, завантаженням картинок і оформленням замовлення — дає список справжніх хибних спрацювань, а не гіпотетичних. Перехід на On має сенс лише після того, як цей список розібрано.

Як працює CRS: не одне правило, а сума

Це ключ до всього, що йде далі. CRS майже ніколи не блокує запит одним правилом. Кожне спрацьоване правило додає запиту балів аномальності, а блокування настає, коли сума переходить поріг. Тому в журналі не один рядок, а кілька, і тому останній з них — правило з ідентифікатором 949110, те, що підбиває підсумок.

Практичний наслідок: виняток треба робити для правила, яке нарахувало бали, а не для 949110. Вимкнете правило-суматор — вимкнете весь набір, і WAF залишиться рядком у конфігураційному файлі.

Другий наслідок — рівень параної. Типово він дорівнює одиниці, і це правильний вибір. Рівні 2 і 3 додають правила, які на звичайних сайтах дають хибні спрацювання за задумом, і вмикати їх варто лише тоді, коли перший рівень налаштовано повністю.

Знайдіть винне правило

Усе потрібне є в журналі аудиту (/var/log/modsec_audit.log) і в журналі помилок вебсервера. Шукайте за часом блокування:

sudo grep -o 'id "[0-9]*"' /var/log/modsec_audit.log | sort | uniq -c | sort -rn | head

Це список частоти правил, які спрацювали. Потім для конкретного ідентифікатора подивіться, що саме його викликало:

sudo grep -A5 'id "942100"' /var/log/modsec_audit.log | head -40

Потрібні три речі: ідентифікатор правила, назва параметра (ARGS:content, ARGS:comment) і шлях запиту. З них будується виняток.

Звичні підозрювані

Список повторюється від сайту до сайту:

  • 942100 — SQL-ін’єкція. Спрацьовує на текстових полях із довгим вмістом: тіло статті, опис товару, коментар. Лапки, дужки та слова на кшталт select здаються детектору підозрілими всередині звичайної прози;
  • 941100 і сімейство 941xxx — XSS. Приходять разом із візуальним редактором: HTML-теги в полі — це і є весь сенс того, як він працює;
  • 920420 — недозволений Content-Type. Ламає API та завантаження файлів: типовий набір дозволених типів вузький, а в старих версіях application/json у ньому не було;
  • 913100 — сканер за User-Agent. Ловить легітимні інструменти разом зі сканерами: моніторинг доступності, curl у ваших власних скриптах;
  • 200002, 200004 — помилки розбору тіла запиту. Зазвичай означають не атаку, а перевищений ліміт розміру тіла, тобто завантаження великого файла.

Три способи зробити виняток

За зростанням грубості. Усі вони йдуть у ваш власний файл (наприклад, /etc/modsecurity/crs/REQUEST-900-EXCLUSION-RULES.conf), а не у файли самого CRS: набір правил оновлюється, і ваші правки зникнуть разом із ним.

Прибрати один параметр з-під одного правила. Найточніший варіант і той, до якого варто прагнути:

SecRuleUpdateTargetById 942100 "!ARGS:content"

Вимкнути правило лише на одному шляху. Саме тоді, коли шумить конкретна сторінка — редактор, імпорт, форма відгуку:

SecRule REQUEST_URI "@beginsWith /admin/post" \
    "id:1001,phase:1,pass,nolog,ctl:ruleRemoveById=942100"

Вимкнути правило повністю. Останній засіб і майже завжди ознака того, що причину так і не знайшли:

SecRuleRemoveById 942100

Різниця між першим і третім суттєва. У першому випадку одне поле — тіло статті — перестає перевірятися на SQL-ін’єкцію одним правилом; у другому й третьому перевірятися перестає весь сайт. Різниця у зусиллях — близько п’яти хвилин.

Після правок перевірте конфігурацію і м’яко перезавантажте:

sudo apachectl configtest && sudo systemctl reload apache2
sudo nginx -t && sudo systemctl reload nginx

Порядок, який економить тиждень

  1. Тиждень у DetectionOnly зі звичайною роботою на сайті, в адмінці та із завантаженнями.
  2. Список частоти правил із журналу аудиту. Ідіть згори вниз — перші три-чотири дають дев’яносто відсотків шуму.
  3. Для кожного: з’ясуйте, який параметр і на якій сторінці. Робіть виняток за параметром, а не за правилом.
  4. І лише тепер SecRuleEngine On.
  5. Раз на місяць заглядайте в блокування: сайт змінився, отже, з’явилися нові хибні спрацювання.

Саме на цьому останньому пункті все зазвичай і розсипається: журнал аудиту — це гігабайти тексту, і руками його ніхто не читатиме. Сенс зібраної панелі в тому, щоб список спрацьованих правил, заблоковані запити й активний набір правил були перед очима, а не діставалися grep-ом. Який вигляд це має, показує демосторінка нижче.