Нічого не зіпсує спокійну нічну зміну на чергуванні так, як скриншот від CEO з повідомленням «403 Forbidden» на сторінці входу в WordPress. Маркетинг не може публікувати. Редактори не можуть завантажувати файли. Клієнти не можуть оформити замовлення. І ваш WAF, який має бути спокійним дорослим у кімнаті, раптом став швейцаром, що виганяє гостей.
Виправлення — не «відключити WAF» і не «додати в білий список усе під /wp-admin/». Правильний підхід — зібрати докази, ізолювати точне правило та налаштувати найменший можливий виняток із захисними обмеженнями. Вам потрібно, щоб WordPress працював, а атаки нудьгували. Обидва варіанти досяжні.
Що фактично відбувається, коли WordPress «блокують WAF»
Коли користувачі кажуть «WAF заблокував WordPress», вони зазвичай мають на увазі одне з чотирьох:
- Справжній інцидент: хтось (або щось) дійсно намагається виконати SQLi/XSS/RCE і WAF це заблокував. WordPress — просто місце приземлення трафіку.
- Хибне спрацьовування: легітимна поведінка WordPress виглядає підозріло. Звичайні підозрювані: HTML редактора, запити REST API, AJAX-ендпоінти, завантаження файлів або сторінки плагінів з дивними параметрами.
- Спрацьовування контролю частоти/поведінки: захист від ботів, захист входу або загальне лімітування запитів спрацьовують на адміністративну активність, cron-завданнях або перевірках стану CDN.
- Невідповідність меж довіри: додаток бачить інші IP-клієнтів, ніж WAF, або проксі змінює cookies/заголовки. Правило, призначене захищати один шар, карає інший.
Більшість проблем WordPress/WAF походить від концентрації ендпоінтів. WordPress пропускає багато складної поведінки через кілька URL:
/wp-login.phpдля автентифікації/wp-admin/admin-ajax.phpдля асинхронної поведінки UI (фронт- і бекенд)/wp-json/для REST API (включно з можливостями Gutenberg/редактора)/xmlrpc.phpісторично використовувався для віддаленого публікування; тепер здебільшого використовується ботами та старими інтеграціями/wp-admin/для всього іншого, що виглядає «адмінським» і тому «атакувальним»
WAF не «розуміють WordPress». Вони розуміють HTTP-запити та шаблони, що часто корелюють з атаками. WordPress генерує контент і параметри, які випадково нагадують атаки. Ваше завдання — звузити невідповідність, не втрачаючи значущого захисту.
Одна провідна ідея, перефразована від John Allspaw: операції — це про дозволяти зміни без шкоди. Це стосується й WAF — безпечні винятки — форма операційної зрілості.
Швидкий план діагностики (перший/другий/третій)
Перший: підтвердьте, що це WAF (не додаток, не CDN, не автентифікація)
- Подивіться на HTTP-статус: 403/406/429 — часті результати WAF, але додатки теж повертають 403.
- Перевірте заголовки WAF (вендорно-специфічні) і ідентифікатор запиту, який можна трасувати.
- Скорелюйте часові мітки та IP-клієнта між CDN/проксі та логами origin.
Другий: ідентифікуйте точне правило та компонент запиту
- Витягніть запис події/аудиту WAF: ID правила/групи, змінну, яка співпала (ARGS, REQUEST_URI, REQUEST_HEADERS), уривок співпавшого вмісту.
- Визначте, чи співпало за URI, параметром запиту, тілом, cookie або заголовком.
- Підтвердіть, чи правило в режимі block або log/count.
Третій: оберіть найменш ризикований важіль налаштувань
- Виправте запит/додаток, якщо він справді дивний (поганий плагін, неправильне кодування, величезні cookies).
- Звузьте область за допомогою URI + методу + стану автентифікації перед тим, як вимикати правило.
- Виключіть найменшу змінну (поодинокий параметр), а не цілу групу правил.
- Підвищуйте пороги аномалій лише коли доведете, що трафік безпечний і маєте компенсуючі контролі.
Цікаві факти та контекст (бо історія повторюється)
- ModSecurity починався на початку 2000-х як модуль Apache — WAF спочатку були «гарячою латкою» для вразливих додатків, а не панацеєю.
- OWASP Core Rule Set (CRS) став дефолтним «загальним» мозком WAF для багатьох стеків; він задуманий широким, а не специфічним для WordPress.
- XML-RPC у WordPress був важливим у кінці 2000-х для віддаленого публікування; сьогодні його часто зловживають для брутфорсу та ампліфікації.
- Gutenberg (блоковий редактор) зсунув WordPress до інтенсивнішого використання REST API; багато «раптових» проблем WAF з’явились після оновлень редактора, бо змінилась форма запитів.
- Ранні розгортання WAF часто працювали в режимі «тільки виявлення» тижнями; блокування відразу викликало збої. Дорослі команди досі роблять етапи для змін WAF як для коду.
- Моделі «позитивної безпеки» (дозволяти відомі хороші шаблони) існували раніше за сучасний маркетинг WAF; ними складніше керувати, але вони надійніші для адмін-ендпоінтів.
- Деякі сигнатури виявлення SQLi старі на десятиліття; атакувальники адаптуються, але й додатки змінюються — хибні спрацьовування часто викликають сучасні JSON-тіла, які трипають на застарілі шаблони.
- CDN популяризували керовані правила WAF і зробили «WAF перед усім» звичним; плюс — масштаб, мінус — ви тепер налагоджуєте розподілений рушій політик.
Принципи безпечного налаштування WAF (без імітації безпеки)
1) Не домовляйтеся з 403; збирайте докази
Кожен WAF має спосіб показати вам, яке правило спрацювало і що саме співпало. Якщо ви цього не бачите, ви працюєте всліпу. Коли зацікавлені сторони вимагають миттєвого рішення, дайте швидку триажну відповідь: «Я можу зупинити кровотечу, обійшовши WAF для цього шляху, але мені потрібно 30 хвилин, щоб зробити це безпечно». Потім зробіть це безпечно.
2) Винятки повинні бути вужчими за ваше терпіння
Хороші винятки обмежені за:
- URI шляху (
/wp-json/проти «всього WordPress») - HTTP-методу (дозволити
GET, але ретельно перевірятиPOST) - Контексту автентифікації (наявна адмін cookie, чи запити з VPN, чи з відомого IdP)
- Імені параметра (виключити поле
content, а не все тіло) - ID правила (відключити одне правило в одному місці, а не весь керований набір)
3) Віддавайте перевагу «виправити тригер» над «вимкнути детектор»
Якщо плагін надсилає параметр, що виглядає як SQLi, бо містить plain-слова select і union, правильним рішенням може бути: коректно кодувати, змінити назви полів, зменшити відображення або санітизувати контент раніше. Налаштування WAF не заміна гігієни додатка.
4) Додайте надійні прості контролі
WordPress набуває великої користі від простих, стабільних заходів: лімітування логінів, фільтрація ботів, MFA, принцип найменшої привілейованості для адміністраторів, блокування невикористовуваних ендпоінтів і оновлення плагінів. Налаштування WAF менш страшне, коли є компенсуючі контролі.
Жарт №1: Вимкнути WAF, щоб виправити WordPress — це як зняти пожежні датчики, бо вас дратує писк. Працює, поки не перестане.
Практичні завдання: команди, виводи та рішення
Нижче практичні завдання, які можна виконати на типовому Linux-origin з Nginx/Apache і ModSecurity, а також кілька перевірок для CDN/WAF. Кожне завдання включає: команду, що означає вивід, і яке рішення прийняти.
Завдання 1: Відтворіть блокування відомим запитом та зафіксуйте заголовки
cr0x@server:~$ curl -kisS https://example.com/wp-login.php | sed -n '1,20p'
HTTP/2 403
date: Fri, 27 Dec 2025 10:14:22 GMT
content-type: text/html; charset=UTF-8
server: cloudflare
cf-ray: 86a1c2d3e4f56789-FRA
Що це означає: HTTP 403 з Cloudflare-заголовком вказує на блокування на краю, а не на origin. Значення cf-ray — ваш трейс-хендл.
Рішення: Витягніть подію WAF за ідентифікатором запиту (або часовою позначкою+IP) перед втручанням у правила origin.
Завдання 2: Перевірте відповідь origin напряму (обійдіть CDN), щоб довести, де блок
cr0x@server:~$ curl -kisS --resolve example.com:443:203.0.113.10 https://example.com/wp-login.php | sed -n '1,20p'
HTTP/2 200
date: Fri, 27 Dec 2025 10:15:01 GMT
content-type: text/html; charset=UTF-8
server: nginx
Що це означає: Origin повертає 200, а edge — 403. Блокує WAF/CDN, а не WordPress.
Рішення: Налаштовуйте WAF на краю. Не витрачайте час на PHP-логи поки що.
Завдання 3: Ідентифікуйте, який ендпоінт блокується (адмін, REST, AJAX, завантаження)
cr0x@server:~$ curl -kisS https://example.com/wp-json/wp/v2/users/me | sed -n '1,15p'
HTTP/2 403
date: Fri, 27 Dec 2025 10:16:11 GMT
content-type: application/json
server: cloudflare
Що це означає: REST API блокується. Це може зламати Gutenberg, медіа та інтерфейси плагінів.
Рішення: Шукайтe керовані правила, що націлені на JSON-тела, заголовки автентифікації або загальні сигнатури «API-атаки»; налаштовуйте для /wp-json/, а не глобально.
Завдання 4: Якщо ModSecurity на origin, підтвердіть, що він увімкнений і в якому режимі
cr0x@server:~$ sudo nginx -T 2>/dev/null | grep -E 'modsecurity|SecRuleEngine' -n
121:modsecurity on;
122:modsecurity_rules_file /etc/nginx/modsec/main.conf;
Що це означає: Nginx зібраний з конектором ModSecurity і він увімкнений.
Рішення: Наступний крок: перевірити, у режимі DetectionOnly чи On, і переглянути журнали аудиту.
Завдання 5: Перевірте налаштування SecRuleEngine
cr0x@server:~$ sudo grep -R --line-number 'SecRuleEngine' /etc/nginx/modsec
/etc/nginx/modsec/main.conf:12:SecRuleEngine On
Що це означає: Запити можуть активно блокуватись на origin.
Рішення: Якщо ви в середині інциденту, розгляньте тимчасовий перехід до DetectionOnly для конкретного розташування під час аналізу (не як загальне вимикання).
Завдання 6: Знайдіть журнал аудиту ModSecurity і підтвердіть, що він отримує події
cr0x@server:~$ sudo grep -R --line-number 'SecAuditLog' /etc/nginx/modsec
/etc/nginx/modsec/main.conf:45:SecAuditLog /var/log/modsec_audit.log
cr0x@server:~$ sudo tail -n 3 /var/log/modsec_audit.log
--c9a1b7d8-H--
Message: Access denied with code 403 (phase 2). Matched "Operator `Rx' with parameter `(?i:(?:union(?:.*)select))'" against variable `ARGS:query' (Value: `union select`) [file "/etc/modsecurity.d/owasp-crs/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf"] [id "942100"] [severity "CRITICAL"] [tag "application-multi"] [hostname "example.com"] [uri "/wp-admin/admin-ajax.php"] [unique_id "1735294592"]
Що це означає: Правило 942100 (категорія SQLi) спрацювало на ARGS:query в admin-ajax.php. Це достатньо конкретно для точного налаштування.
Рішення: Не відключайте всі правила SQLi. Створіть цілеспрямоване виключення для параметра query на цьому ендпоінті, якщо ви перевірили, що воно безпечне і очікуване.
Завдання 7: Скорелюйте запит у access-логах (метод, розмір, user agent, IP клієнта)
cr0x@server:~$ sudo awk '$7 ~ /admin-ajax\.php/ {print $1,$4,$6,$7,$9,$10,$12}' /var/log/nginx/access.log | tail -n 5
198.51.100.23 [27/Dec/2025:10:16:31 +0000] "POST /wp-admin/admin-ajax.php 403 721 "Mozilla/5.0"
198.51.100.23 [27/Dec/2025:10:16:34 +0000] "POST /wp-admin/admin-ajax.php 403 721 "Mozilla/5.0"
Що це означає: Повторювані 403 для POST до admin-ajax. IP клієнта стабільний; виглядає як реальний браузер.
Рішення: Якщо один і той самий блок зачіпає багато реальних користувачів з різних IP, це, ймовірно, хибне спрацьовування. Якщо це один IP, що штурмує — можлива атака, не робіть налаштувань, блокуйте.
Завдання 8: Витягніть значення співпавших параметрів з журналу аудиту, щоб побачити, що тригерить
cr0x@server:~$ sudo grep -n 'Matched "Operator' -n /var/log/modsec_audit.log | tail -n 2
4128:Message: Access denied with code 403 (phase 2). Matched "Operator `Rx' with parameter `(?i:(?:union(?:.*)select))'" against variable `ARGS:query' (Value: `union select`) [id "942100"] [uri "/wp-admin/admin-ajax.php"] [unique_id "1735294592"]
Що це означає: Присутнє буквальне значення union select. Це може бути шкідливим навантаженням або синтаксисом пошуку/фільтра в інтерфейсі (погана практика, але трапляється).
Рішення: Перевірте функцію, яка генерує такий запит. Якщо це легітимний синтаксис пошуку, потрібні компенсуючі контролі (тільки для автентифікованих, перевірки nonce, лімітування) перед виключенням.
Завдання 9: Підтвердіть контекст nonce/автентифікації WordPress для викликів admin-ajax
cr0x@server:~$ curl -kisS -X POST https://example.com/wp-admin/admin-ajax.php \
-d 'action=test_action&_ajax_nonce=deadbeef&query=union%20select' | sed -n '1,25p'
HTTP/2 403
date: Fri, 27 Dec 2025 10:18:12 GMT
content-type: text/html; charset=UTF-8
Що це означає: Все ще блокує WAF; ви не доходите до перевірки nonce WordPress.
Рішення: Якщо плануєте виключення для admin-ajax, звузьте його до автентифікованих адмін-сесій (де можливо) або до конкретних дій/параметрів, а не робіть загальний обхід admin-ajax.
Завдання 10: Для Apache + ModSecurity підтвердіть, який набір правил завантажено (CRS) і підказки версій
cr0x@server:~$ sudo apachectl -M 2>/dev/null | grep -i security
security2_module (shared)
cr0x@server:~$ sudo grep -R --line-number 'owasp-crs' /etc/apache2 | head
/etc/apache2/mods-enabled/security2.conf:15:IncludeOptional /usr/share/modsecurity-crs/owasp-crs.load
Що це означає: Ви використовуєте OWASP CRS. ID правил на кшталт 942100 відповідають CRS SQLi.
Рішення: Використовуйте механізми виключення, що підтримує CRS; уникайте редагування файлів правил вендора напряму (це ускладнює оновлення).
Завдання 11: Створіть вузьке виключення ModSecurity (приклад) та перевірте синтаксис
cr0x@server:~$ sudo tee /etc/nginx/modsec/wordpress-exclusions.conf >/dev/null <<'EOF'
# Narrow exclusion: only for admin-ajax.php, only remove one parameter from one rule
SecRule REQUEST_URI "@streq /wp-admin/admin-ajax.php" "id:1001001,phase:1,pass,nolog,ctl:ruleRemoveTargetById=942100;ARGS:query"
EOF
cr0x@server:~$ sudo grep -R --line-number 'wordpress-exclusions' /etc/nginx/modsec/main.conf
72:Include /etc/nginx/modsec/wordpress-exclusions.conf
cr0x@server:~$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Що це означає: Ви видалили параметр query з інспекції правилом 942100 лише для того URI. Все інше досі інспектується.
Рішення: Перезавантажте і протестуйте. Якщо атаки перейдуть на інші параметри, ви побачите нові спрацьовування правил. Це нормально; ви ітеруєте на підставі доказів.
Завдання 12: Перезавантажте і повторно протестуйте точний фейловий запит
cr0x@server:~$ sudo systemctl reload nginx
cr0x@server:~$ curl -kisS -X POST https://example.com/wp-admin/admin-ajax.php \
-d 'action=test_action&_ajax_nonce=deadbeef&query=union%20select' | sed -n '1,25p'
HTTP/2 200
date: Fri, 27 Dec 2025 10:19:44 GMT
content-type: text/html; charset=UTF-8
Що це означає: WAF більше не блокує цей запит. Тепер потрібно переконатися, що WordPress відкидає неправильні nonces і неавтентифіковану поведінку, як задумано.
Рішення: Підтвердіть, що шар додатку все ще валідовує автентифікацію/nonce; якщо ні, ваше «виправлення» WAF стало експлойт-ланню.
Завдання 13: Перевірте, що WordPress досі блокує неавтентифіковані небезпечні дії
cr0x@server:~$ curl -kisS -X POST https://example.com/wp-admin/admin-ajax.php \
-d 'action=delete_user&user_id=1' | sed -n '1,30p'
HTTP/2 200
date: Fri, 27 Dec 2025 10:20:12 GMT
content-type: text/html; charset=UTF-8
0
Що це означає: Багато AJAX-ендпоінтів WordPress повертають 0 для неавторизованих запитів. Це досить добре; означає, що ви дійшли до WordPress і він відхилив дію.
Рішення: Якщо бачите успішну відповідь для привілейованих дій без автентифікації — зупиніться. Відкотуйте виключення і спочатку виправте перевірку автентифікації/nonce.
Завдання 14: Перевірте, чи великі cookies не тригерять ліміти заголовків (хитрий тригер WAF)
cr0x@server:~$ curl -kisS https://example.com/ | awk 'tolower($0) ~ /^set-cookie:/ {print length($0), $0}' | head
148 Set-Cookie: wordpress_test_cookie=WP%20Cookie%20check; path=/
312 Set-Cookie: wp-settings-1=libraryContent%3Dbrowse; path=/; expires=Sat, 26 Dec 2026 10:20:55 GMT
Що це означає: Невеличкі, але якщо бачите дуже довгі рядки cookie (тисячі байтів), деякі WAF/проксі відхилятимуть запити пізніше, коли заголовки Cookie розростаються.
Рішення: Якщо cookies дуже великі — зменшіть їх (очистка плагінів, звуження області cookie, уникати зберігання стану в cookies), а не налаштовуйте WAF на прийом абсурдних заголовків.
Завдання 15: Виявляйте 429 (rate limit) vs 403 (сигнатура) в логах
cr0x@server:~$ sudo awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head
981 200
114 403
62 404
27 429
Що це означає: Деякі запити обмежуються по частоті (429). Це інший шлях налаштування, ніж хибні спрацьовування сигнатур.
Рішення: Якщо редактори отримують 429 під час нормальної роботи в адмінці, відкоригуйте ліміти за ендпоінтом і методом. Не послаблюйте правила SQLi/XSS, щоб вирішити лімітування частоти.
Завдання 16: Підтвердіть збереження реального IP клієнта (налаштування WAF провалиться, якщо IPи некоректні)
cr0x@server:~$ sudo grep -R --line-number 'real_ip_header|set_real_ip_from' /etc/nginx | head -n 20
/etc/nginx/conf.d/realip.conf:1:real_ip_header CF-Connecting-IP;
/etc/nginx/conf.d/realip.conf:2:set_real_ip_from 173.245.48.0/20;
/etc/nginx/conf.d/realip.conf:3:set_real_ip_from 103.21.244.0/22;
Що це означає: Nginx довіряє заголовку й конкретним діапазонам проксі. Без цього лімітування та кореляція WAF можуть бути безглуздими.
Рішення: Якщо всі клієнти в логах мають IP проксі — виправте обробку реального IP перед налаштуванням поведінкових правил.
Шаблони налаштування, що працюють (і що вони варті)
Шаблон A: Виключити один параметр з одного правила на одному ендпоінті
Це золотий стандарт для хибних спрацьовувань. Ви залишаєте правило активним всюди інде. Інші правила на цьому ендпоінті теж лишаються активними. Ви також зберігаєте аудит-логування.
Найкраще для: полів контенту Gutenberg/редактора, полів пошуку, ключів JSON, що випадково виглядають як код.
Вартість: Потрібно підтримувати невеликий перелік «відомо шумних параметрів». Це не серйозна вартість; це називається відповідальністю за систему.
Шаблон B: Розділити політики для адмінів і публіки
Адмін-ендпоінти поводяться інакше. Їх теж треба захищати по-іншому. У багатьох організаціях найкращий крок — поставити адмін-панель за додатковими контролями:
- Вимагати MFA / SSO
- Обмежити доступ VPN або корпоративними вихідними IP (якщо можливо)
- Тісніше лімітування логінів; вільніше щодо POST-тілів редактора, де є легітимний HTML
Найкраще для: сайтів з багатьма редакторами та великою адмін-активністю.
Вартість: Більше об’єктів політик та тестування. Все ж дешевше за час простою та панічні whitelist-правила.
Шаблон C: Тимчасово перевести керовані правила з «block» у «count» — хірургічно
Іноді ви не знаєте, чи це хибне спрацьовування, поки не зберете більше подій. Перекладіть одну керовану групу правил у режим count/detect-only для одного ендпоінта на короткий період.
Найкраще для: інцидентів з високим бізнес-імпактом і нечіткими доказами.
Вартість: Короткостроковий ризик. Потрібні компенсуючі заходи: лімітування і моніторинг під час вікна.
Шаблон D: Блокуйте те, чим не користуєтесь (це зменшує ризик і шум WAF)
Якщо вам не потрібен /xmlrpc.php, блокуйте його на краю або на origin. Те саме для невикористовуваних REST-маршрутів чи застарілих ендпоінтів. Менша поверхня атаки — менше дивних payload і менше конфліктів правил.
Жарт №2: XML-RPC — як той старий принтер у кутку: ніхто не зізнається, що ним користується, але він все одно спричиняє інциденти.
Три міні-історії з реальних корпоративних систем
Міні-історія 1: Інцидент через хибне припущення
Середня компанія перенесла маркетинговий сайт на WordPress за керованим WAF. У чек-листі міграції було «увімкнути керовані правила, встановити блокування, відправити в прод». Припущення було, що керовані правила «безпечні за замовчуванням». Зазвичай так і є. Поки ні.
У перший понеділок редактори почали використовувати новий плагін для побудови сторінок, який зберігав макети як великі JSON-блоби через /wp-json/. WAF почав блокувати запити як «protocol attack: invalid JSON» і «XSS в тілі», бо конструктор зберігав HTML-фрагменти і inline-стилі всередині JSON-рядків.
Інженери вирішили, що це баг WordPress. Маркетинг звинуватив інженерів у «блокуванні креативу». Безпека подумала, що маркетинг завантажує шкідливе ПЗ. Усі були неправі по-своєму.
Виправлення не полягало у відключенні WAF. Вони витягли журнали подій WAF, знайшли два шумні правила і виключили конкретні ключі JSON на маршруті /wp-json/, зберігши XSS-правила активними в інших місцях. Також додали ліміт розміру і нормалізацію кодування на межі додатка. Інцидент закінчився, як і суперечка.
Міні-історія 2: Оптимізація, що обернулася проти
Більша організація вирішила «оптимізувати продуктивність», агресивніше кешуючи на CDN. Діяли так: кешувати все під /wp-json/ на 10 хвилин. Вони також тримали WAF жорстким для API, бо «API небезпечні». Два розумні рішення поєдналися у хаос.
Редактори бачили випадкові помилки авторизації та періодичні 403. Деякі запити кешувалися як 200 (з автентифікованого контексту) і подавались неавтентифікованим клієнтам, що викликало перевірки ботів та невідповідність політик WAF. Тим часом nonces WordPress почали випадково втрачати чинність, бо UI змішував кешовані і живі відповіді.
Інженери спочатку налаштовували WAF, бо 403 означало «проблема WAF». Це зробило ситуацію гіршою: тепер неправильний кешований контент ширився. Справжня причина — кешування ендпоінта, який ніколи не слід кешувати без точних правил vary та сегментації автентифікації.
Вони відкотили кешування для автентифікованих REST-маршрутів, кешували лише явні публічні GET-маршрути і посилили WAF навколо входу та XML-RPC. Продуктивність покращилася, кількість помилок зменшилася, і команда витягнула урок: «швидше» — не функція, якщо вона неправильна.
Міні-історія 3: Нудна, але правильна практика, що врятувала день
Регульований бізнес запускал WordPress для порталу клієнтів. Їхня команда безпеки вимагала, щоб кожна зміна WAF проходила маленьким пайплайном: 24 години в режимі логування, перегляд топ-співпадінь, потім включення в режимі блокування. Звучало бюрократично. Воно ж і дозволяло спати спокійно.
Одного дня оновлення плагіна змінило адмін-форму так, що контент відправлявся параметром template з шматками HTML і шорткодами. WAF почав співпадати це як «PHP injection» на адмін-сторінках. Оскільки процес змін WAF включав базові дашборди і режим staging, вони помітили сплеск перш ніж переводити правило в блокування у проді.
Вони реалізували виключення, обмежене для автентифікованих адмін-URI і конкретного параметра, а також додали unit-тест, який відправляє точно такий payload через WAF у CI. Нудно. Передбачувано. Ефективно.
Коли аудитор запитав «як ви запобігаєте екстреному відключенню контролів безпеки», у них була відповідь, яка не була обіцянкою. Це були докази.
Поширені помилки: симптом → корінь проблеми → виправлення
1) Редактори не можуть зберегти дописи; Gutenberg показує «Updating failed»
Симптом: періодичні 403/406 на POST-запитах до /wp-json/.
Корінь проблеми: правило WAF спрацьовує на JSON-тело (HTML-фрагменти, inline-скрипти або CSS) або стримкі правила парсингу JSON.
Виправлення: Виключте конкретні ключі JSON/параметри для конкретних ID правил на /wp-json/ POST, зберігайте логування. Підтвердіть, що автентифікація і перевірка nonce WordPress все ще працюють.
2) Завантаження медіа не проходить з 403; тільки великі файли не залежать
Симптом: малі файли завантажуються, великі — падають; іноді також з’являється 413.
Корінь проблеми: обмеження розміру тіла на WAF/CDN/origin; правила парсингу multipart; таймаути при інтеграції антивірусу.
Виправлення: Узгодьте ліміти між шарами (CDN, WAF, Nginx/Apache, PHP). Краще підвищити ліміти, ніж обходити інспекцію для завантажень; якщо вимушено обходите — додайте строгі перевірки типу файлу і сканування на шкідливість.
3) wp-admin працює через VPN, але не вдома
Симптом: 403 лише для деяких ISP/регіонів; адмін виглядає як «бот».
Корінь проблеми: захист від ботів або правила репутації занадто агресивні; відсутні challenge/cookie через налаштування приватності; або origin неправильно читає IP-клієнта й спрацьовує лімітування.
Виправлення: Виправте обробку реального IP; налаштуйте правила бот-захисту спеціально для /wp-login.php і /wp-admin/; розгляньте step-up challenge лише при вході.
4) GET-запити REST API блокуються, але нормальні сторінки працюють
Симптом: публічний сайт завантажується, але інтеграції падають.
Корінь проблеми: керована група «API protection» блокує шаблони, поширені в параметрах REST.
Виправлення: Дозвольте конкретні REST-маршрути і методи; виключайте лише проблемні імена параметрів для конкретних ID правил; додайте лімітування і перевірки автентифікації для чутливих ендпоінтів.
5) Все під /wp-admin/ тимчасово внесено в білый список, а потім ніколи не відмінено
Симптом: команда безпеки потім скаржиться; ви знаходите правило обходу, що діє місяцями.
Корінь проблеми: усунення інциденту без плану відкату; відсутність терміну дії у екстрених політиках.
Виправлення: Додавайте явний термін дії для екстрених винятків і оповіщення про «наявні правила обходу». Інтегруйте це в управління змінами, а не в людську пам’ять.
6) WAF блокує легітимний HTML у контенті посту
Симптом: збереження посту тригерить XSS-правила.
Корінь проблеми: WAF бачить HTML і думає «атака». Контент WordPress буквально є HTML. Шокуюче, я знаю.
Виправлення: Виключіть поля контенту лише для ендпоінтів редактора; переконайтеся, що санітизація KSES WordPress увімкнена і ви не даєте unfiltered_html всім користувачам.
7) Брутфорс логінів все ще успішний після налаштувань
Симптом: налаштування зменшили блокування, але підвищили успіх атак.
Корінь проблеми: ви послабили захист, пов’язаний з автентифікацією (лімітування, перевірки ботів), під час усунення хибних спрацьовувань в інших місцях.
Виправлення: Розділіть політики: суворіше для /wp-login.php, /xmlrpc.php і адмінки; вимірювані винятки для редактора/REST-пейлоадів.
Контрольні списки / покроковий план
Покроковий план налаштування без створення діри
- Зафіксуйте один фейловий запит: часову мітку, IP клієнта, URI, метод, ідентифікатор запиту (edge trace header, якщо є).
- Доведіть шар, що блокує: edge чи origin за допомогою
curl --resolveабо прямого доступу до origin з довіреної мережі. - Отримайте подію WAF: ID правила/групи, змінна, уривок payload, дія (block/challenge/log).
- Класифікуйте подію:
- Схоже на атаку з випадкових IP? Розглядайте як справжній інцидент.
- Відбувається для автентифікованих редакторів з різних IP? Ймовірно хибне спрацьовування.
- Переважно 429? Це лімітування, а не сигнатури.
- Оберіть найменший важіль:
- Виключити один параметр для одного ID правила на одному URI, або
- Звузити виняток за методом + URI + контекстом автентифікації, або
- Тимчасово перевести в режим count/detect-only одну групу для одного ендпоінта під час збору доказів.
- Додайте компенсуючі контролі коли послаблюєте інспекцію:
- Гарантуйте, що перевірки nonce/автентифікації WordPress виконуються
- Лімітуйте логіни та admin-ajax за IP/користувачем
- Блокуйте невикористовувані ендпоінти, наприклад XML-RPC
- Протестуйте точний провал та кілька зловживань: повторіть фейловий запит; також спробуйте очевидні payload і переконайтесь, що вони все ще блокуються де потрібно.
- Моніторте дрейф: нові топ-спрацьовування WAF, зростання 5xx, збільшення помилок автентифікації або різке падіння блокувань (ознака занадто великого обходу).
- Документуйте виняток: ендпоінт, ID правила, причина, власник, дата закінчення і план відкату.
Операційний чекліст для продакшн-готовності
- Журнали WAF доступні, пошукові та зберігаються достатньо довго для розслідувань.
- Адмін-ендпоінти мають сильніші контролі, ніж публічні (розділення політик).
- Екстрені правила обходу мають термін дії та оповіщення.
- Існує лімітування для
/wp-login.phpта/xmlrpc.php. - Реальний IP клієнта правильно передається в логи origin і інструменти безпеки.
- Стейджинг може відтворити поведінку WAF (або у вас є безпечне вікно «count-only» для збору доказів).
Питання та відповіді
1) Чи варто просто додати /wp-admin/ у білий список в WAF?
Ні. Саме там відбуваються найцінніші дії. Якщо потрібно зменшити фрикцію, звужуйте винятки до конкретних адмін-ендпоінтів і параметрів, а захист входу тримайте строгим.
2) Чи безпечно відключити одне ID правила OWASP CRS?
Іноді так, але робіть це як хірургічне втручання: відключайте або видаляйте цільові частини найменшого обсягу, і тільки для ендпоінта, де доведено шум. Не робіть «ruleRemoveById» глобально, якщо вам не подобається пояснювати інциденти потім.
3) Чому admin-ajax.php тригерить так багато правил WAF?
Бо це високонавантажений POST-ендпоінт з великою кількістю параметрів і діями, визначеними плагінами. Атакувальники його люблять, плагіни його зловживають, а WAF помічає обидва випадки.
4) Чи варто блокувати xmlrpc.php?
Якщо вам це не потрібно — так, блокуйте на краю і на origin. Якщо потрібно — сильно лімітуйте та дозволяйте лише відомим клієнтам.
5) Як зрозуміти, чи це хибне спрацьовування чи реальна атака?
Дивіться на контекст: чи це трафік автентифікованих редакторів, стабільні user agent, відтворюваність із нормальних робочих процесів? Чи це випадкові IP-спреї з очевидними payload? Використовуйте журнали аудиту: змінна, що співпала, та уривок payload зазвичай прояснюють ситуацію.
6) Чи може кешування спричинити блокування WAF?
Непрямо — так. Неправильне кешування автентифікованих REST-відповідей може створювати невідповідності і повторні повтори запитів, що тригерять правила ботів/лімітування. Виправте стратегію кешування перед послабленням правил безпеки.
7) Як налаштовувати WAF, не втрачаючи видимості?
Зберігайте логування активним навіть при виключеннях. Використовуйте режим count/detect-only для коротких вікон збору доказів. Будуйте дашборди по топ-ID правил і уражених URI, щоб винятки не ставали невидимими назавжди.
8) Який найбезпечніший спосіб обробки HTML редактора, що тригерить XSS-правила?
Виключіть конкретне поле контенту на конкретному ендпоінті редактора з конкретних XSS-правил і переконайтесь, що санітизація WordPress і права користувачів налаштовані правильно.
9) Що робити, якщо вендор WAF не показує співпавшого payload?
Тоді вам потрібне краще логування на якомусь шарі, який ви контролюєте (журнали аудиту origin WAF, дебаг-логи зворотного проксі з обережністю або staging). Налаштовувати без даних про співпад — це гадання з паперами.
Практичні наступні кроки
Зробіть три речі сьогодні, в такому порядку:
- Отримайте трасованість: забезпечте, щоб кожне блокування давало ідентифікатор запиту і ID правила, які можна шукати. Якщо це не так — виправте логування перед наступним інцидентом, щоб вам не довелося «тимчасово обходити» себе в глухий кут.
- Розділіть політику за ендпоінтами: розглядайте
/wp-login.php,/xmlrpc.php,/wp-admin/,/wp-json/і/admin-ajax.phpяк різні продукти з різними профілями ризику. - Впровадьте вузькі виключення з терміном дії: виключайте параметри лише з конкретних правил, а не цілі групи. Додайте власника і дату закінчення, зробіть план відкату нудним і передбачуваним.
Ваш WAF має бути скальпелем, а не пов’язкою на очі. Якщо він блокує WordPress, вам не потрібно менше захисту — вам потрібен кращий підхід.