Brute-force атаки на WordPress: захистіть вхід, не заблокуйте себе

Було корисно?

Іноді ваш сайт на WordPress працює без проблем. А потім одного ранку CPU завантажений під зав’язку, PHP-FPM виглядає так, ніби він на тренуванні, а логи нескінченно додають POST /wp-login.php. Реальні користувачі не можуть ввійти. Коефіцієнт потрапляння в кеш падає. Атака не «витончена», вона «безжальна».

Підступ у тому, щоб блокувати brute‑force без того, щоб зробити власний вхід самозашкоджуючим відмовним сервісом. Посилення входу в WordPress легко зробити неправильно і дивовижно нудно — але правильно. Зробимо правильно.

Цікаві факти й історія (чому це болісно)

  • wp-login.php став гарячою ціллю, бо він передбачуваний. Зловмисникам подобаються стабільні URL; WordPress зробив доступ до адмінки простим і, отже, перелічуваним.
  • XML‑RPC (xmlrpc.php) був введений для віддаленої публікації. Він також дав атакуючим спосіб пакетної перевірки автентифікації через system.multicall.
  • «admin» як ім’я користувача був звичним. Старі інсталяції WordPress мали його за замовчуванням, і погані звички виживають — особливо на забутих стендових сайтах.
  • Brute‑force часто — побічна справа. Багато кампаній не націлені спеціально на ваш сайт; вони прочісують інтернет у пошуках слабких паролів для побудови ботнетів.
  • Credential stuffing обганяє brute‑force. Атакувальники часто використовують влиті пари логін/пароль; ваша «політика сильних паролів» не допоможе, якщо користувачі повторно використовують паролі деінде.
  • Rate limiting існує довше за більшість веб‑фреймворків. Мережеві інженери обмежували зловмисних клієнтів задовго до того, як «WAF» став окремим продуктом.
  • Блокування може стати відмовою в обслуговуванні. Наївна логіка «блок після 3 спроб на 24 години» дозволяє атакуючим заблокувати реальних користувачів, намагаючись їх імена користувачів.
  • 403 проти 404 має операційне значення. Повернення 404 для /wp-login.php для всіх, крім дозволених джерел, зменшує шум сканування й спрощує аналіз логів.
  • Більшість компрометацій WordPress не походять від brute‑force. Уразливі плагіни і теми — значніший джерело реальних зломів; посилення входу треба робити, але воно не панацея.

Жарт №1: Brute‑force атакувальники як малюки біля зачинених дверей — безкінечно оптимістичні, голосні і переконані, що проблема в тобі.

Як виглядає brute‑force у продакшні

Сигнали, які ви помітите першими

На невеликому VPS brute‑force проявляється як стрибки load average і повні PHP‑вікна. На великому хості це тонше: більше 499/504, підвищений TTFB і затримки на ендпойнті входу. Симптом з боку користувача зазвичай «не можу ввійти» або «сайт повільний», але реальна проблема в тому, що кожна спроба входу викликає дорогі обробки: хешування паролів, налаштування сесії, звернення до БД, можливо якісь плагіни, що не повинні бути в цьому шляху запиту.

Куди б’ють атакуючі

  • /wp-login.php — класична форма входу.
  • /wp-admin/ — редиректи до входу; все одно генерує навантаження.
  • /xmlrpc.php — ендпойнт віддаленої публікації, часто зловживається для підбору паролів.
  • REST‑ендпойнти, що виводять імена користувачів — це не обхід автентифікації, але допомагає в перелічуванні.

Чому «просто блокувати IP» не працює

Атаки розподілені. IP‑адреси змінюються. Часто вони приходять з домашніх мереж або хмарних провайдерів. Якщо ваша стратегія захисту потребує ручних блокувань IP, ви не захищаєте — ви займаєтеся археологією логів.

Ваша справжня мета

Вам треба зменшити кількість спроб автентифікації до низького, передбачуваного рівня, водночас гарантуючи принаймні два незалежні шляхи відновлення доступу адміністратора. Саме тут більшість людей спотикаються.

Операційна цитата для стікера: «Надія — це не стратегія.» — Gene Kranz

Швидкий план діагностики (перше/друге/третє)

Це «ви на виклику, 02:00, і хтось із маркетингу на звʼязку» версія.

Перше: підтвердіть, що це brute‑force, і визначте точку входу

  • Перевірте access‑логи на найпопулярніші шляхи й швидкість запитів.
  • Підтвердіть, чи це wp-login.php, xmlrpc.php або обидва.
  • Шукайте «щільний цикл»: той самий URI, той самий метод, багато 200/302/403.

Друге: захистіть потужність, перш ніж гнатися за елегантністю

  • Додайте тимчасове обмеження швидкості на краю (CDN/WAF) або на Nginx/Apache.
  • За потреби тимчасово заблокуйте xmlrpc.php і перевірте, чи нічого критичного не зламається.
  • Покращіть зрозумілість логування: винесіть ендпойнти автентифікації в окремий формат або файл логів.

Третє: впровадьте надійні контрзаходи з шляхом відновлення

  • Додайте у білый список IP‑и адміністраторів (або ще краще: вимагайте VPN / zero‑trust) для /wp-admin/ та /wp-login.php.
  • Увімкніть 2FA для облікових записів адміністраторів.
  • Встановіть Fail2ban (або еквівалент), що реагує на веб‑логи.
  • Вимкніть або обмежте XML‑RPC і перевірте Jetpack/мобільні робочі процеси, якщо ви їх використовуєте.

Увузлення не завжди в «CPU повільний». Це «ваш шлях автентифікації не обмежений». Виправте це спочатку; потім оптимізуйте.

Практичні завдання: команди, виводи та рішення

Ці завдання передбачають Linux з Nginx або Apache, плюс systemd. Виконуйте їх на веб‑хості (або на зворотньому проксі, якщо такий є). Кожне завдання містить: команду, приклад виводу, що означає вивід і яке рішення прийняти.

Завдання 1: швидко визначити топ шляхів запитів

cr0x@server:~$ sudo awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head
  48219 /wp-login.php
  11703 /xmlrpc.php
   4122 /
   3088 /wp-admin/
    944 /wp-json/wp/v2/users

Що означає вивід: /wp-login.php домінує. /xmlrpc.php також під ударом. Ендпойнт REST користувачів вказує на перелічування імен користувачів.

Рішення: Віддайте пріоритет обмеженню швидкості та контролю доступу для wp-login.php і xmlrpc.php. Розгляньте обмеження видимості REST‑ендпойнтів користувачів.

Завдання 2: виміряти швидкість запитів до wp-login.php

cr0x@server:~$ sudo awk '$7=="/wp-login.php"{print $4}' /var/log/nginx/access.log | cut -d: -f1-3 | sort | uniq -c | sort -nr | head
   982 [27/Dec/2025:01
   944 [27/Dec/2025:00
   901 [26/Dec/2025:23

Що означає: Приблизно 900–980 звернень на годину (або на хвилину, залежно від формату логів). У будь‑якому разі: забагато для реального сайту.

Рішення: Встановіть початковий ліміт (наприклад: 5 запитів/хв на IP для wp-login.php) і спостерігайте за побічними ефектами.

Завдання 3: знайти найактивніші IP

cr0x@server:~$ sudo awk '$7=="/wp-login.php"{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head
   710 203.0.113.44
   688 198.51.100.72
   621 192.0.2.19

Що означає: Кілька IP дуже голосні. Багато атак не будуть такими концентрованими, але коли вони такі — тимчасові блоки купують вам час.

Рішення: Використовуйте короткочасні фаєрвол‑блоки як тимчасовий захід. Не робіть з ручного блокування щоденну практику.

Завдання 4: перевірити, чи зловживають XML‑RPC через multicall

cr0x@server:~$ sudo grep -c "POST /xmlrpc.php" /var/log/nginx/access.log
11703

Що означає: Ендпойнт активний. Щоб підтвердити multicall, огляньте тіла запитів на WAF/проксі або увімкніть обмежене логування тіла запиту (обережно; це може витікати креденшіали).

Рішення: Якщо вам не потрібен XML‑RPC, вимкніть його. Якщо потрібен — жорстко обмежте.

Завдання 5: перевірити мікс 401/403/200 для розуміння ситуації

cr0x@server:~$ sudo awk '$7=="/wp-login.php"{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -nr
  31001 200
  14112 302
   1106 403

Що означає: Багато завантажень сторінки входу повертають 200. 302 можуть вказувати на редиректи (часто успішний вхід, але також може бути редирект назад на вхід при невдачі залежно від налаштувань).

Рішення: Не гадати. Використовуйте логи додатку або плагін для логування автентифікацій, якщо потрібна точна статистика «невдалих паролів»; інакше — обмежуйте швидкість незалежно.

Завдання 6: підтвердити навантаження PHP‑FPM (поширене вузьке місце)

cr0x@server:~$ sudo systemctl status php8.2-fpm --no-pager
● php8.2-fpm.service - The PHP 8.2 FastCGI Process Manager
     Loaded: loaded (/lib/systemd/system/php8.2-fpm.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2025-12-26 23:12:08 UTC; 2h 11min ago
   Main PID: 1240 (php-fpm8.2)
     Status: "Processes active: 43, idle: 2, Requests: 184201, slow: 57, Traffic: 1.2req/sec"

Що означає: Ви близькі до максимуму активних воркерів; є повільні запити. Brute‑force може виснажити воркери і задушити реальний трафік.

Рішення: Негайно обмежте доступ до ендпойнтів автентифікації. Тільки після цього думайте про тонке налаштування FPM.

Завдання 7: переглянути помилки Nginx щодо таймаутів апстріму

cr0x@server:~$ sudo tail -n 8 /var/log/nginx/error.log
2025/12/27 01:18:21 [error] 2011#2011: *9921 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 198.51.100.72, server: example.com, request: "POST /wp-login.php HTTP/1.1", upstream: "fastcgi://unix:/run/php/php8.2-fpm.sock", host: "example.com"

Що означає: Шлях автентифікації достатньо повільний, щоб досягти таймаутів — класика при brute‑force. Таймаути також викликають ретраї, що погіршує навантаження.

Рішення: Тротлінг, потім зменшення роботи в шляху автентифікації (плагіни, зовнішні виклики), і потім налаштування таймаутів.

Завдання 8: додати прицільний rate limit в Nginx для wp-login.php

Спочатку перевірте включення конфігів Nginx. Потім реалізуйте щось мінімальне і оборотне.

cr0x@server:~$ sudo nginx -T 2>/dev/null | grep -n "http {" -n | head
32:http {

Що означає: Ви знаєте, де знаходиться контекст http; зони limit_req потрібно визначати там.

Рішення: Додайте limit_req_zone у http, потім застосуйте в location для wp-login.php.

cr0x@server:~$ sudo bash -lc 'cat >/etc/nginx/conf.d/wp-login-rate-limit.conf <<"EOF"
limit_req_zone $binary_remote_addr zone=wp_login:10m rate=5r/m;
EOF'
cr0x@server:~$ sudo bash -lc 'cat >/etc/nginx/snippets/wp-login-protect.conf <<"EOF"
location = /wp-login.php {
  limit_req zone=wp_login burst=10 nodelay;
  include snippets/fastcgi-php.conf;
  fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
EOF'
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

Що означає: Конфіг розбирається. Ви ще не підтвердили поведінку, але безпечно перезавантажувати.

Рішення: Перезавантажте і спостерігайте за 429 відповідями та скаргами користувачів.

cr0x@server:~$ sudo systemctl reload nginx

Завдання 9: перевірити, чи лімітування працює

cr0x@server:~$ sudo grep -E "limiting requests" /var/log/nginx/error.log | tail -n 3
2025/12/27 01:22:09 [error] 2011#2011: *10441 limiting requests, excess: 5.900 by zone "wp_login", client: 203.0.113.44, server: example.com, request: "POST /wp-login.php HTTP/1.1", host: "example.com"

Що означає: Nginx активно тротлить. Це захист потужності.

Рішення: Залишайте це, але не зупиняйтеся на цьому — саме rate limiting не вберігає від повільного credential stuffing назавжди.

Завдання 10: додати безпечний allowlist‑шлюз для wp-admin (не зламавши фронтенд)

Цей підхід різкий і ефективний: доступ до адмінки лише з VPN‑еґресу або корпоративного NAT. Це незручно для мандрівних користувачів, але операційно розумно для бізнесу.

cr0x@server:~$ sudo bash -lc 'cat >/etc/nginx/snippets/wp-admin-allowlist.conf <<"EOF"
location ^~ /wp-admin/ {
  allow 203.0.113.10;
  allow 203.0.113.11;
  deny all;
  try_files $uri $uri/ /index.php?$args;
}
EOF'
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

Що означає: Синтаксис ок. Пам’ятайте, що /wp-admin/admin-ajax.php використовується темами/плагінами на фронтенді.

Рішення: Явно виключіть admin-ajax.php з allowlist‑у, якщо сайт його публічно використовує.

cr0x@server:~$ sudo bash -lc 'cat >/etc/nginx/snippets/wp-admin-ajax-open.conf <<"EOF"
location = /wp-admin/admin-ajax.php {
  include snippets/fastcgi-php.conf;
  fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
EOF'

Завдання 11: відключити XML‑RPC на веб‑шарі (швидко) і перевірити вплив

cr0x@server:~$ sudo bash -lc 'cat >/etc/nginx/snippets/disable-xmlrpc.conf <<"EOF"
location = /xmlrpc.php {
  return 403;
}
EOF'
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

Що означає: Це повністю блокує XML‑RPC. Jetpack, деякі мобільні додатки і інтеграції можуть залежати від нього.

Рішення: Якщо вам потрібен XML‑RPC, не вимикайте — обмежте доступ відомими IP або додайте додаткові перевірки. Інакше залишайте його вимкненим.

Завдання 12: встановити і перевірити Fail2ban для шаблонів wp-login

cr0x@server:~$ sudo apt-get update -y && sudo apt-get install -y fail2ban
Reading package lists... Done
Building dependency tree... Done
fail2ban is already the newest version (1.0.2-2).

Що означає: Fail2ban доступний. Тепер потрібен фільтр і jail, налаштовані під ваш формат логів.

Рішення: Використовуйте невелику тривалість бану і розумне findtime; ви вже обмежуєте швидкість, це для повторних порушників і зменшення розподіленого шуму.

cr0x@server:~$ sudo bash -lc 'cat >/etc/fail2ban/filter.d/nginx-wp-login.conf <<"EOF"
[Definition]
failregex = ^<HOST> - .* "POST /wp-login\.php HTTP/1\.[01]" (200|302) .*
ignoreregex =
EOF'
cr0x@server:~$ sudo bash -lc 'cat >/etc/fail2ban/jail.d/nginx-wp-login.local <<"EOF"
[nginx-wp-login]
enabled = true
port = http,https
filter = nginx-wp-login
logpath = /var/log/nginx/access.log
maxretry = 10
findtime = 600
bantime = 3600
EOF'
cr0x@server:~$ sudo fail2ban-client reload
OK
cr0x@server:~$ sudo fail2ban-client status nginx-wp-login
Status for the jail: nginx-wp-login
|- Filter
|  |- Currently failed: 2
|  |- Total failed:     118
|  `- File list:        /var/log/nginx/access.log
`- Actions
   |- Currently banned: 3
   |- Total banned:     7
   `- Banned IP list:   198.51.100.72 203.0.113.44 192.0.2.19

Що означає: У вас автоматичні бани. Це не «безпека вирішена», але суттєво зменшує шум.

Рішення: Тримайте бани помірними; не заблокуйте офісний NAT на годину, бо хтось забув пароль.

Завдання 13: підтвердити користувачів WordPress і прибрати очевидні ризики

cr0x@server:~$ cd /var/www/html && sudo -u www-data wp user list --fields=ID,user_login,roles
+----+------------+----------------------+
| ID | user_login | roles                |
+----+------------+----------------------+
|  1 | admin      | administrator        |
| 12 | editor1    | editor               |
| 17 | seo-team   | administrator        |
+----+------------+----------------------+

Що означає: Якщо у вас ще є admin як логін, ви полегшуєте життя атакуючим.

Рішення: Створіть нового адміна з непримітним логіном, перенесіть привілеї і видаліть або понизьте admin. (І: увімкніть 2FA.)

Завдання 14: аварійне відновлення — безпечно скинути пароль адміністратора

cr0x@server:~$ cd /var/www/html && sudo -u www-data wp user update admin --user_pass='REDACTED-strong-password'
Success: Updated user 1.

Що означає: Ви можете відновити доступ без phpMyAdmin або ручного редагування бази.

Рішення: Якщо у вас немає WP‑CLI, встановіть його зараз — до того, як воно знадобиться о 02:00.

Завдання 15: перевірити, що ви себе не заблокували (нудний, але необхідний тест)

cr0x@server:~$ curl -I -s https://example.com/wp-login.php | head -n 5
HTTP/2 200
date: Sat, 27 Dec 2025 01:28:11 GMT
content-type: text/html; charset=UTF-8
cache-control: no-store, no-cache, must-revalidate, max-age=0

Що означає: Ендпойнт доступний звідки ви тестуєте. Якщо ви застосували allowlist‑и, тестуйте ще й з дозволеної мережі.

Рішення: Запустіть другий тест з небажаної IP (або попросіть колегу через LTE), щоб підтвердити очікувану поведінку deny.

Патерни посилення, що не блокують вас

1) Віддавайте перевагу «шлюзовим» контролям над «загадуванням»

Rate limiting і Fail2ban реагують на поведінку. Allowlists та вимога VPN повністю запобігають експозиції. Якщо це корпоративна адмінка WordPress для співробітників, правильний крок простий: не виставляйте wp-admin в інтернет. Поставте його за VPN, identity‑aware proxy або хоча б IP‑allowlist.

Якщо ви керуєте спільнотою з багатьма мандруючими адміністраторами, allowlist не спрацює. Тоді більше спирайтеся на: 2FA, passwordless (де доступно) і жорстке обмеження швидкості.

2) Зробіть wp-login дешевим у виконанні

Навіть з rate limiting, кожен запит має бути максимально дешевим у виконанні.

  • Уберіть важкі плагіни, що підключаються до аутентифікації й роблять зовнішні виклики.
  • Переконайтеся, що працює object caching, щоб зменшити навантаження на БД при повторних запитах.
  • Вимкніть ендпойнти для перелічування користувачів (де можливо), щоб атакуючі не могли легко підтверджувати імена користувачів.

3) Визначте політику щодо XML‑RPC явно

XML‑RPC не «зло». Він просто застарілий і часто непотрібний. Якщо ви не використовуєте Jetpack, мобільний додаток WordPress або старі інтеграції — блокуйте його.

Якщо він потрібен, обмежте доступ:

  • Додавайте в allowlist лише відомі IP (якщо у вашої інтеграції стабільний egress).
  • Окремо лімітуйте його, відмінно від wp-login.php.
  • Надавайте перевагу контролям на рівні програми, які вимикають зловживання system.multicall (деякі плагіни безпеки мають таку опцію).

4) Використовуйте 2FA серйозно

2FA не зупиняє brute‑force спроби; він робить успішний brute‑force значно менш імовірним. Операційний виграш великий: ви можете трохи мʼяче ставитися до локдаунів без підвищення ризику.

Дві операційні правила:

  • Вимагайте 2FA для облікових записів адміністраторів принаймні.
  • Зберігайте резервні коди в менеджері паролів, доступному для чергових співробітників.

5) Не робіть «приховування» wp-login головним захистом

Зміна URL входу може зменшити шум. Вона не вирішує основну проблему і може зламати інтеграції, кеші та операційні інструкції. Використовуйте це лише як вторинний захід і тільки якщо маєте надійний план відновлення.

6) Майте щонайменше два шляхи відновлення

Локдаути переживні, якщо ви їх запланували. Оберіть два (або більше) і тестуйте їх щоквартально:

  • WP‑CLI доступ під www-data (або відповідним FPM‑користувачем) для скидання паролів і керування плагінами.
  • Аварійний VPN‑профіль «break‑glass», збережений у захищеному сховищі.
  • Бастіон‑хост зі стабільним IP, що в allowlist‑і для адмін‑ендпойнтів.
  • Процедура доступу до БД для створення адмін‑користувача (останній засіб; небезпечно в поспіху).

Жарт №2: Єдине, що простіше за блокування атакуючих — це заблокувати вашого CEO за п’ять хвилин до презентації перед правлінням.

Три корпоративні міні‑історії (як люди насправді помиляються)

Міні‑історія 1: Інцидент через неправильне припущення

Компанія A мала інсталяцію WordPress, яка «не важлива». Там був блог вакансій і кілька лендингів. Головний продукт був в іншому місці, тому хост WordPress жив у занедбаному куточку інфраструктури.

Інженер помітив brute‑force і додав агресивне правило: блокувати будь‑який IP, який потрапляє на /wp-login.php більше трьох разів на день. Здавалося безпечним: «реальні користувачі так не помиляються», — міркували вони. Неправильне припущення було не технічним, а людським. Реальні користувачі абсолютно можуть помилятися стільки разів, особливо після міграції SSO, зміни менеджера паролів або довгої відпустки.

Атакуючому не треба було вгадувати паролі. Достатньо було мати список імен користувачів. Вони робили по три спроби для кожного відомого адміністратора з ротації IP. Плагін безпеку сумлінно заблокував кожен обліковий запис адміністратора на 24 години. Раптом ніхто не міг публікувати вакансії, рекрутери масово надсилали скриншоти «рахунок заблоковано» ніби це баг продукту.

Виправлення вимагало відкотити політику блокувань, ручного розблокування і нового дизайну: rate limiting на IP (не на імʼя користувача), помірні вікна банів, 2FA і доступ адміністраторів через VPN. Урок простий: захист від brute‑force має карати боти, а не людей.

Міні‑історія 2: Оптимізація, що дала зворотній ефект

Компанія B працювала за реверс‑проксі і хотіла зменшити навантаження на бекенд. Хтось увімкнув агресивне кешування сторінок і намагався «кешувати все». Сайт став швидшим — до наступної хвилі brute‑force.

Кеш‑шар не кешував wp-login.php (добре), але кешував деякі редиректи і сторінки помилок дивним чином. Під навантаженням клієнти почали отримувати непослідовні редиректи між /wp-admin/ і /wp-login.php. Дехто застряг у циклах. Проксі‑ключі кешу не містили заголовка, що відрізняв певні відповіді, і проксі з радістю віддавав неправильне швидко.

І стало ще гірше: проксі був налаштований автоматично повторювати upstream‑помилки. Коли PHP‑FPM почав таймити під brute‑force, проксі робив ретраї. Це подвоїло трафік на найгарячішому ендпойнті. Усі хотіли як краще; система не знала про добрі наміри.

Виправлення було «менш розумним, але правильним»: явно обходити кеш для всіх шляхів автентифікації, вимкнути автоматичні ретраї для не‑ідемпотентних запитів (наприклад POST на логін), і реалізувати rate limiting на проксі. Продуктивність і передбачувана поведінка повернулися. Мораль: кешування — не контроль безпеки, а ретраї — не безкоштовні.

Міні‑історія 3: Нудна, але правильна практика, що врятувала день

Компанія C мала рутину: кожна інфраструктурна зміна WordPress супроводжувалась протестованим чеклістом відновлення. Не вікі‑сторінка, яку ніхто не читає — реальний рунаук із сухим прогоном щоквартально. Двоє адміністраторів практикували: «симулювати локдаут», «відновити доступ через WP‑CLI», «перевірити резервні 2FA», «підтвердити allowlist», «перевірити, що трафік не‑адмінів не зачеплений».

Коли нове правило WAF розгорнули глобально, воно почало викликати перевірки на логіни, які не дружили з деякими корпоративними мережами. Користувачі скаржилися, що вхід «крутиться вічно». Команда WAF спочатку підозрювала хост WordPress, бо так зазвичай буває.

Інженер на виклику пішов за рунауком. Спочатку перевірив стан бекенду. Далі — коди стану ендпойнтів автентифікації. Потім порівняв поведінку з бастіона і з нормальним клієнтом. Різниця вивела прямо на WAF. Вони відкотили конкретне правило для wp-login.php, залишивши rate limits і захист від ботів для решти сайту.

Жодних героїчних подвигів. Жодних гадань. Просто відпрацьований шлях відновлення і контрольоване масштабування. «Нудна» рутина і була причиною, чому ніхто не провів ніч у Slack, обговорюючи правила фаєрвола.

Поширені помилки: симптом → причина → виправлення

1) Симптом: Адміни раптово не можуть увійти; трафік підтримки росте

Причина: Блокування за імʼям користувача (або занадто строгі глобальні блокування) дозволяє атакуючим відключати легітимні акаунти без знання паролів.

Виправлення: Лімітуйте за IP і за ендпойнтом, а не за іменем користувача. Тримайте коротші бани. Вимагайте 2FA для адміністраторів. Використовуйте allowlist‑шлях доступу для адміністраторів (VPN/бастіон).

2) Симптом: Сайт повільний; досягнуто max children PHP‑FPM

Причина: Необмежені спроби входу виснажують PHP‑воркери; дорогі хук‑функції при аутентифікації підсилюють витрати.

Виправлення: Додайте обмеження на веб‑шарі (limit_req або еквівалент), потім аудитуйте плагіни на наявність важких хуків і прибирайте їх із шляху входу.

3) Симптом: Блокування wp-admin ламає фронтенд‑фічі

Причина: /wp-admin/admin-ajax.php використовується публічно фронтенд‑компонентами; блокування всього /wp-admin/ ламає AJAX‑функції.

Виправлення: Дозвольте публічний доступ до admin-ajax.php (і будь‑яких інших необхідних ендпойнтів), залишивши обмеження для решти /wp-admin/.

4) Симптом: Ви вимкнули XML‑RPC і щось «таємниче» перестало працювати

Причина: Jetpack, мобільні додатки або старі інтеграції залежали від XML‑RPC.

Виправлення: Або знову увімкніть з IP‑allowlist або rate limiting, або замініть інтеграцію на REST‑альтернативи. Приймайте рішення щодо XML‑RPC свідомо.

5) Симптом: Ви додали WAF‑челендж і логіни циклятся або провалюються мовчки

Причина: Бот‑челенджі можуть ламати POST‑поточення, куки або не‑браузерні клієнти; інколи корпоративні проксі видаляють заголовки.

Виправлення: Виключіть /wp-login.php з інтерактивних челенджів; використовуйте rate limits і правила за репутацією. Тестуйте з різних мереж.

6) Симптом: Fail2ban банить усіх за NAT

Причина: Багато користувачів ділять одну публічну IP; maxretry спрацьовує на агрегаті.

Виправлення: Збільшіть пороги, скоротіть тривалість бану і більше покладайтеся на rate limiting. Розгляньте gating адмінки через VPN, щоб зменшити колізії через спільні IP.

7) Симптом: Ви «сховали» wp-login і тепер інтеграції ламаються, і ніхто не памʼятає URL

Причина: Безпека‑через‑приховування без операційної дисципліни.

Виправлення: Якщо ви змінюєте URL входу — документуйте це в внутрішньому рунауці і тримайте break‑glass шлях (WP‑CLI + VPN/бастіон). Розглядайте це як опціональний засіб зменшення шуму, а не як основний захист.

Чеклісти / покроковий план

Фаза 0 (сьогодні): зупиніть кровотечу, не зламаючи систему

  1. Підтвердіть цільові ендпойнти в логах: wp-login.php, xmlrpc.php, wp-admin.
  2. Додайте rate limiting на веб‑шарі для wp-login.phpxmlrpc.php, якщо він увімкнений).
  3. Вимкніть XML‑RPC, якщо він вам не потрібен. Якщо потрібен — обмежте його.
  4. Перевірте насичення PHP‑FPM і переконайтеся, що таймаути не породжують ретраїв.
  5. Переконайтеся, що реальний адмін все ще може увійти з принаймні двох мереж.

Фаза 1 (цей тиждень): зменшіть експозицію й додайте ідентичність

  1. Поставте wp-admin за шлюзом: VPN, identity‑aware proxy або IP‑allowlist.
  2. Вимагайте 2FA для адміністраторів і зберігайте резервні коди в захищеному сховищі з контролем доступу.
  3. Видаліть очевидні облікові записи: приберіть admin, аудитуйте неактивних адміністраторів, дотримуйтеся принципу найменших привілеїв.
  4. Розгорніть Fail2ban, налаштований під ваші логи і реалії NAT.
  5. Додайте моніторинг для стрибків запитів до ендпойнтів автентифікації, 429 та виснаження воркерів FPM.

Фаза 2 (цей місяць): зробіть систему стійкою і тестованою

  1. Створіть runbook «break‑glass»: скидання пароля через WP‑CLI, вимкнення плагінів, кроки для відкату WAF.
  2. Проведіть тренування локдауту: спеціально симулюйте відмову allowlist і відпрацюйте відновлення.
  3. Перегляньте вектор атак плагінів/тем: оновлюйте, видаляйте abandonware і зменшуйте хуки автентифікації.
  4. Розгляньте винесення ендпойнтів аутентифікації під окремий захист (edge rules, суворіший rate limit, окреме логування).

FAQ

1) Чи варто просто заблокувати wp-login.php повністю?

Якщо у вас контрольований шлях для адміністраторів (VPN/бастіон/identity proxy), так — блокуйте його для публічного інтернету і пропускайте лише через шлюз. Якщо у вас мандруючі адміністратори — не блокуйте повністю; використовуйте rate limit + 2FA + Fail2ban.

2) Чи варто змінювати URL входу?

Воно зменшує шум, але не ризик. Використовуйте це тільки після налаштування rate limiting, 2FA і протестованого плану відновлення. Інакше ви міняєте одну проблему на нову «де логін?».

3) Чому я бачу brute‑force навіть на маленькому, непомітному сайті?

Бо це автоматизоване сканування. Атакувальникам не треба знати про вашу існування; їм достатньо знати, що сайт працює на WordPress. Передбачувані ендпойнти — їх улюблені цілі.

4) Чи зупинить 2FA brute‑force спроби?

Ні. Він не зупиняє самі спроби, але значно зменшує ймовірність успішного входу. Все одно потрібні rate limits, щоб захистити потужність і запобігти відмовам у обслуговуванні.

5) Чи можна безпечно вимкнути XML‑RPC?

Часто так. Але перевірте, чи не використовуєте ви Jetpack, мобільний додаток або старі інтеграції. Якщо не впевнені — вимкніть у період низького трафіку і спостерігайте логи та бізнес‑процеси.

6) Чому блокування /wp-admin/ інколи ламає головну сторінку?

Зазвичай це ламає фічі, що викликають /wp-admin/admin-ajax.php з фронтенду. Виключіть цей ендпойнт або мігруйте від admin‑ajax, якщо можливо.

7) Чи достатньо Fail2ban сам по собі?

Ні. Це хороший другий рівень захисту. Перший — обмеження на веб‑шарі і зменшення експозиції. Fail2ban також слабкий проти сильно розподілених атак і середовищ зі спільним NAT.

8) Як уникнути само‑блокування при додаванні allowlist‑ів?

Завжди майте другий метод доступу: бастіон зі стабільним IP або VPN. Перевіряйте з дозволеної і недозволеної мережі перед тим, як відійти. І тримайте WP‑CLI під рукою для скидання облікових даних.

9) Який розумний rate limit для wp-login.php?

Почніть з близько 5 запитів/хв на IP з невеликим burst‑ом, потім налаштовуйте. Якщо у вас багато легітимних користувачів за NAT, збільшіть burst або rate і більше покладайтеся на 2FA та WAF‑репутацію.

10) Що робити, якщо атакуючий використовує валідні облікові дані (credential stuffing)?

Rate limiting уповільнює атаки, 2FA запобігає більшості захоплень акаунтів, а моніторинг (невдалі входи, дивні гео, impossible travel) ловить те, що пройшло. Також: заохочуйте використання менеджерів паролів і видаляйте неактивні облікові записи.

Наступні кроки, які ви можете зробити сьогодні

Зробіть три речі, і ваш вхід WordPress перестане бути мішенню:

  1. Обмежте запити до ендпойнтів автентифікації на веб‑шарі. Це негайно захищає потужність.
  2. Заховайте wp-admin за VPN/бастіоном/identity proxy, якщо це бізнес‑адмінка, а не публічна спільнотна адмінка.
  3. Тримайте реальні шляхи відновлення: доступ WP‑CLI, резервні коди 2FA і протестований runbook. «Ми завжди можемо зайти по SSH» — це не план відновлення; це казка на ніч.

Потім зробіть непоказну, але корисну роботу: перевірте облікові записи адміністраторів, приберіть очевидні логіни, вимкніть те, що не використовуєте (особливо XML‑RPC) і моніторьте швидкість запитів до входу так само, як ви моніторите вільне місце на диску. Не тому, що це захопливо. Тому що це працює.

← Попередня
Правило 80% у ZFS: міф, правда і справжня «зона небезпеки»
Наступна →
Proxmox “TASK ERROR: timeout waiting for …”: виявлення реального джерела тайм‑ауту

Залишити коментар