ZIM-НИЙ SAAALEЗимние скидки: до −50% на старт и −20% на продление
до 31.01.2026 Подробнее
Выберите продукт

Защита wp-login.php: фильтры fail2ban, лимиты в Nginx и 2FA

Разбираем практическую схему защиты wp-login.php и xmlrpc.php от перебора паролей без боли для живых пользователей. Настроим лимиты запросов в Nginx, фильтры и баны в fail2ban, включим обязательную 2FA. Дадим готовые конфиги, способы проверки и нюансы эксплуатации.
Защита wp-login.php: фильтры fail2ban, лимиты в Nginx и 2FA

Если у вас есть WordPress, вы уже видите постоянные попытки bruteforce на wp-login.php и xmlrpc.php. Простое скрытие URL не решает проблему надолго: сканеры быстро находят вход. Рабочая комбинация выглядит так: срезаем трафик лимитами (Nginx rate limit), отсекаем агрессивных источников на уровне брандмауэра (fail2ban), а внутри приложения делаем вход бесполезным без второго фактора (2FA). Ниже — пошагово, с примерами конфигов и рекомендациями для продакшна.

Почему именно три слоя: Nginx + fail2ban + 2FA

Один слой защиты почти всегда либо слишком мягкий (не снижает нагрузку на PHP), либо слишком жёсткий (ломает легитимный трафик), либо бессилен против украденных паролей. Комбинация даёт баланс:

  • Nginx моментально режет всплески и защищает PHP-FPM от штормов запросов.
  • fail2ban выносит источники bruteforce в бан по сетевому уровню — экономит CPU и I/O.
  • 2FA в WordPress нейтрализует украденные пароли и утечки.

Результат: устойчивость к атакам, предсказуемая нагрузка и минимальные ложные срабатывания при корректных лимитах.

Слой 1. Rate limit в Nginx для wp-login и xmlrpc

Базовая зона и применение

Создадим зону лимитов по IP и применим её к /wp-login.php и /xmlrpc.php. Выберите базовые пороги, от которых удобно стартовать на бою, а затем адаптируйте под аудиторию.

# http
limit_req_zone $binary_remote_addr zone=wp_zone:10m rate=30r/m;
limit_req_zone $binary_remote_addr zone=xmlrpc_zone:10m rate=10r/m;

# Ограничение параллельных соединений от одного IP
limit_conn_zone $binary_remote_addr zone=perip:10m;

Применение к нужным локациям:

# server
location = /wp-login.php {
    limit_req zone=wp_zone burst=10 nodelay;
    limit_conn perip 5;
    include fastcgi_params;
    # Дальше — ваш fastcgi_pass и пр.
}

location = /xmlrpc.php {
    limit_req zone=xmlrpc_zone burst=5 nodelay;
    limit_conn perip 2;
    return 444;
}

Возврат 444 для xmlrpc — агрессивный, но эффективный выбор, если он не нужен легальным интеграциям. Если xmlrpc требуется, оставьте обработку PHP, но сохраняйте низкие лимиты.

Мягкое обходное для своих IP

Чтобы не мешать редакторам и администраторам из доверенных сетей, добавьте «тумблер» лимита для своих адресов. Удобно делать через geo и управлять лимитом параметром if= в limit_req.

# http
geo $remote_addr $wp_limit_on {
    default 1;
    192.0.2.10 0;        # пример: офис
    198.51.100.0/24 0;   # пример: VPN-пул
}

# server
location = /wp-login.php {
    limit_req zone=wp_zone burst=10 nodelay if=$wp_limit_on;
    limit_conn perip 5;
    include fastcgi_params;
}

Если сервер работает за обратным прокси/CDN, настройте корректную идентификацию клиента: real_ip_header и доверенные адреса через set_real_ip_from, чтобы $remote_addr был реальным IP пользователя.

Логирование 429 и пользовательский ответ

Видимый «человеческий» ответ при 429 помогает понять ситуацию легитимному пользователю.

# http или server
error_page 429 = @limit429;
location @limit429 {
    add_header Retry-After 60 always;
    return 429 "Too Many Requests\n";
}

Если вы на управляемом хостинге и нет root-доступа, проверьте, доступны ли лимиты на уровне панели или поддержки. На отдельном VDS можно гибко управлять Nginx и брандмауэром под свой трафик.

Применение лимитов Nginx к wp-login и xmlrpc

Слой 2. Блокировки fail2ban по логам Nginx

Установка и запуск

sudo apt-get update
sudo apt-get install fail2ban -y
sudo systemctl enable --now fail2ban

Фильтр по access.log для wp-login и xmlrpc

Простой, но действенный подход — считать агрессивным поведение, когда IP многократно шлёт POST на /wp-login.php или долбит /xmlrpc.php. Создайте фильтр.

# /etc/fail2ban/filter.d/wordpress-login.conf
[Definition]
# Примеры для комбинированного формата Nginx ("combined")
failregex = ^<HOST> - - \[.*\] "POST /wp-login\.php HTTP/.*" (200|302)
            ^<HOST> - - \[.*\] "POST /xmlrpc\.php HTTP/.*" (200|403|444)
ignoreregex =

Шаблон выше универсален, но подстройте его под ваш log_format. Если включены другие поля или иной порядок колонок — скорректируйте регулярки.

Jail с нарастающим временем бана

# /etc/fail2ban/jail.d/wordpress-login.conf
[wordpress-login]
enabled = true
port = http,https
filter = wordpress-login
logpath = /var/log/nginx/access.log
backend = auto
findtime = 10m
maxretry = 10
bantime = 1h
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 24h

Если на сервере используется nftables по умолчанию, укажите бэкенд действий:

# /etc/fail2ban/jail.d/actions-nft.conf
[DEFAULT]
action = nftables-multiport[name=wp, port="http,https"]

Проверка фильтра и статуса

sudo fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/wordpress-login.conf
sudo fail2ban-client reload
sudo fail2ban-client status
sudo fail2ban-client status wordpress-login

Если Nginx стоит за обратным прокси/CDN, убедитесь, что в access.log записывается реальный клиентский IP. Иначе fail2ban будет банить адрес прокси.

На уровне сервера жёсткие политики SSH и фаерволла хорошо дополняют эту схему. Подробнее см. материал Безопасность VDS: SSH и firewall.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Добавочный фильтр на 429

Полезно банить тех, кто регулярно попадает под лимит Nginx (код 429):

# /etc/fail2ban/filter.d/nginx-429.conf
[Definition]
failregex = ^<HOST> - - \[.*\] ".*" 429
ignoreregex =

# /etc/fail2ban/jail.d/nginx-429.conf
[nginx-429]
enabled = true
port = http,https
filter = nginx-429
logpath = /var/log/nginx/access.log
findtime = 5m
maxretry = 20
bantime = 30m

Слой 3. 2FA в WordPress

Даже с лимитами и банами пароль может быть украден вне сайта. Двухфакторная аутентификация (2FA) закрывает этот сценарий. Оптимальный вариант для WordPress — TOTP (аутентификаторы по одноразовым кодам).

  • Включите 2FA для всех админов и редакторов, а при возможности — сделайте обязательным.
  • Сохраните резервные коды офлайн, обучите команду процедуре восстановления.
  • Синхронизируйте время сервера (NTP/chrony): TOTP чувствителен к сдвигам времени.
  • Проверьте, распространяется ли 2FA на REST и xmlrpc. Если нет — отключите или ограничьте /xmlrpc.php, используйте «пароли приложений» и жёсткие лимиты.

2FA не заменяет парольную политику. Минимум: уникальные длинные пароли и менеджер паролей для команды.

Как fail2ban блокирует IP на основе логов Nginx

Как слои работают вместе

Порядок срабатывания важен для эффективности:

  • Nginx первым режет частоту запросов и закрывает xmlrpc при необходимости. На этом этапе PHP даже не просыпается.
  • fail2ban читает логи и выносит в бан IP, которые не внемлют лимитам и продолжают долбить wp-login/xmlrpc.
  • Даже если пароль украли и попытки единичные (под лимитами), 2FA остановит вход.

Для ускорения реакции учтите 429 в фильтрах fail2ban (см. выше), чтобы агрессоры улетали в бан быстрее.

Практические пресеты лимитов

Стартовые значения, на которых редко бывают жалобы пользователей, и которые заметно снижают шум bruteforce:

  • /wp-login.php: rate=30r/m, burst=10, limit_conn perip 5.
  • /xmlrpc.php: rate=10r/m, burst=5, а лучше — return 444 при ненужности.
  • fail2ban: maxretry=10 за 10m, bantime=1h с инкрементом до суток.

Оцените свою пиковую аудиторию: если у вас много редакторов, расширьте allowlist или сделайте обходной лимит для их подсетей.

Учёт IPv6, прокси и формат логов

  • IPv6: включите блокировки и для него (nftables/iptables v6). Проверьте, что fail2ban видит IPv6 в логах.
  • Обратные прокси/CDN: корректно настраивайте real_ip_header и set_real_ip_from, а также логируйте реальный клиентский адрес.
  • Единый формат логов: храните access.log в одном формате и укажите его в фильтре. Любые изменения — синхронно правьте regex.

Тестирование и отладка

Проверьте, что лимиты Nginx срабатывают:

# 10 быстрых POST-запросов должны вызывать 429 часть времени
for i in $(seq 1 10); do curl -s -o /dev/null -w "%{http_code}\n" -X POST http://127.0.0.1/wp-login.php; done

Смотрите логи и статус банов:

sudo tail -f /var/log/nginx/access.log
sudo fail2ban-client status wordpress-login
sudo fail2ban-client set wordpress-login unbanip 203.0.113.5

Надёжность и эксплуатация

  • Память зон limit_req_zone: 10 МБ хватает примерно на сотни тысяч ключей; если у вас много уникальных IP — увеличьте.
  • Ротация логов: убедитесь, что fail2ban видит новые файлы после logrotate. При необходимости перезагружайте fail2ban.
  • Recidive: подключите «рецидив» для долговременных нарушителей — jail, отслеживающий ранее забаненных.
  • Мониторинг: отслеживайте количество 429, скорость банов и число активных записей в зонах ограничений.

Быстрый чеклист внедрения

  1. Включите лимиты Nginx для /wp-login.php и /xmlrpc.php, настройте allowlist и страницу 429.
  2. Разверните fail2ban, добавьте фильтры по wp-login, xmlrpc и, опционально, по 429.
  3. Настройте 2FA в WordPress, включите для ролей администратор/редактор, подготовьте резервные коды.
  4. Проверьте корректность реального IP в логах при работе за прокси/CDN.
  5. Протестируйте нагрузкой, посмотрите логи и отрегулируйте пороги.

Такой трёхслойный щит решает 95% обычных атак на WordPress: вы разгружаете PHP, быстро блокируете агрессоров и делаете вход бессмысленным без второго фактора. Остальные 5% — это эксплуатация уязвимостей плагинов и тем: держите обновления в порядке и не давайте злоумышленникам второго шанса.

Поделиться статьей

Вам будет интересно

Nginx upstream и DNS: как обновлять IP без stale cache и таймаутов OpenAI Статья написана AI (GPT 5)

Nginx upstream и DNS: как обновлять IP без stale cache и таймаутов

Когда Nginx проксирует на upstream по имени, он может «залипать» на старом IP: контейнер пересоздали, запись A поменялась, а прокс ...
Kubernetes Terminating и finalizers: как разморозить namespace, CRD и PV OpenAI Статья написана AI (GPT 5)

Kubernetes Terminating и finalizers: как разморозить namespace, CRD и PV

Если namespace, CRD или PV застряли в Terminating, почти всегда виноваты finalizers и недоработавшие контроллеры. Разберём диагнос ...
SSHD MaxStartups и rate limit: как снизить CPU от brute force и DDoS на SSH OpenAI Статья написана AI (GPT 5)

SSHD MaxStartups и rate limit: как снизить CPU от brute force и DDoS на SSH

Когда SSH засыпают ботами, растут CPU, число полуоткрытых сессий и задержки входа. Разберём MaxStartups и LoginGraceTime в OpenSSH ...