Как защитить WordPress от брутфорса: полная настройка
Как выглядит брутфорс WordPress
Брутфорс — поток запросов POST /wp-login.php с автоматическим перебором логинов и паролей. Рядом с ним используется credential stuffing — проверка пар логин/пароль из ранее украденных баз. Типичная картина в access-логе сервера:
grep "wp-login" /var/log/nginx/access.log | tail -50
По логу видно три характерные вещи:
- Много неудачных попыток входа — десятки и сотни запросов с одного IP или с множества адресов за короткое время.
- Разные IP — атака часто идёт с ботнета: адреса распределены по разным сетям, поэтому блокировка одного адреса почти ничего не решает.
- Нагрузка на сервер — каждый запрос доходит до PHP, занимает воркер, обращается к базе. Даже неудачная атака делает сайт заметно медленнее.
Отдельная точка атаки — xmlrpc.php: метод system.multicall позволяет слать сотни попыток входа одним запросом и ускоряет перебор. В документации WordPress xmlrpc.php указан как частая цель брутфорса.
Сам факт большого количества попыток входа ещё не означает компрометацию сайта. При уникальном сильном пароле и 2FA вероятность успешного входа резко снижается. Но брутфорс всё равно опасен: он создаёт нагрузку, занимает PHP-воркеры, увеличивает обращения к базе данных.
Уровень 1. Уникальный пароль и 2FA
Первый уровень — учётные данные. Если у администратора пароль admin123 или qwerty, никакой плагин не спасёт: перебор рано или поздно его подберёт.
- Уникальный пароль — 16+ символов, не повторяющийся на других сайтах. Сгенерировать:
openssl rand -base64 24. Не используйте фразы, имена, даты и пароли с других сайтов. - 2FA для всех администраторов — даже если пароль украден или подобран, без одноразового кода TOTP войти нельзя. Это самый сильный уровень защиты.
Если используемый плагин поддерживает passkeys (WebAuthn), их можно использовать как более устойчивую к фишингу альтернативу обычным TOTP-кодам.
Плагины двухфакторной аутентификации:
- WP 2FA — TOTP, позволяет принудительно включить 2FA для всех пользователей роли Administrator.
- Wordfence Security — 2FA, лимит попыток входа, сканирование файлов на малварь. Отдельный плагин Wordfence Login Security прекращён — его не ставить.
- Two-Factor — минималистичный вариант без лишних настроек.
Использование логина admin нежелательно: его легко угадать, а WordPress рекомендует удалять или понижать legacy-аккаунты. Само по себе это не критическая уязвимость при уникальном пароле и 2FA. Если удаляете — сначала создайте нового администратора, иначе потеряете доступ.
После включения 2FA выйдите из админки и войдите заново с кодом из приложения. Если код не запросился — плагин не активирован или 2FA выключен для вашей роли.
Уровень 2. Ограничение попыток входа
Плагин считает неудачные входы с IP и блокирует адрес после заданного числа попыток. Это ломает линейный перебор: бот упирается в блокировку и переключается на другой сайт.
- Limit Login Attempts Security (бывший Limit Login Attempts Reloaded) — классика. Ориентир: 4 попытки, блокировка на 60 минут, после 8 попыток — на 24 часа. Пороги подбирайте под себя: слишком низкие заблокируют собственную работу.
- Loginizer — проще: 3 попытки, блок на 15 минут.
- Wordfence Security — умеет добавлять капчу при высокой частоте запросов к странице входа, а не только блокировать по количеству попыток.
Важный нюанс: плагин работает уже внутри PHP. Каждый запрос доходит до движка и потребляет ресурсы. При серьёзной атаке WordPress рекомендует предпочитать ограничение на уровне сервера или CDN — это следующий уровень.
Запрос, отсечённый до PHP, не занимает воркер и не трогает базу. Если сайт за Cloudflare или на VPS — начните с уровня 3, плагин оставьте как дополнительный слой.
Уровень 3. Rate limiting на сервере и CDN
Это эффективный уровень защиты от нагрузки: запрос можно отсечь до выполнения PHP. Для VPS подходит rate limiting в Nginx, для сайтов за CDN — правила Cloudflare.
Nginx: limit_req для wp-login.php
Принцип — ограничить частоту запросов к странице входа с одного IP:
limit_req_zone $binary_remote_addr zone=wplogin:10m rate=5r/m;
location = /wp-login.php {
limit_req zone=wplogin burst=10 nodelay;
# здесь существующая обработка PHP:
# fastcgi_pass, include fastcgi.conf и т.д. — не менять
}
rate=5r/m задаёт среднюю скорость в 5 запросов в минуту, а burst=10 позволяет кратковременный всплеск до 10 запросов. Параметры rate и burst подбирайте под фактический трафик: если админкой пользуются несколько человек, 5r/m может быть мало.
Конфигурация PHP-обработки на вашем сервере отличается: другой сокет PHP-FPM, свой fastcgi.conf, свои параметры. Никогда не заменяйте существующий location целиком. Добавьте только директиву limit_req внутрь уже работающего блока wp-login.php — либо вынесите её в отдельный location с повторением существующей обработки.
Cloudflare: Custom Rule для страницы входа
В Cloudflare можно создать правило для /wp-login.php, которое блокирует или отправляет подозрительные запросы на проверку (Managed Challenge): например, блокировать запросы из выбранных стран, для остальных — challenge. Доступные условия и действия зависят от тарифа Cloudflare.
fail2ban на VPS
Принцип: отслеживать access-лог и банить IP после серии подозрительных запросов за короткий промежуток. Фильтр пишется под формат вашего лога и фактическую логику авторизации. По одному HTTP-коду определить неудачный вход нельзя: WordPress может вернуть HTTP 200 и при неправильном пароле. Regex на любой POST /wp-login.php ненадёжен — он банит и нормальные входы. Если не готовы разбираться с фильтром — используйте limit_req выше, он проще и не зависит от формата лога.
Уровень 4. XML-RPC: отключить, если не используется
XML-RPC нужен старым приложениям и части сервисов Jetpack. Если вы не используете такие сервисы, отключите: атака через system.multicall позволяет объединять большое количество вызовов в одном HTTP-запросе и может существенно повысить эффективность перебора.
На Apache (.htaccess):
<Files "xmlrpc.php">
Require all denied
</Files>
На Nginx — в server-блоке:
location = /xmlrpc.php {
return 403;
}
Некоторые функции Jetpack используют XML-RPC. После отключения проверьте, что Jetpack работает. Если XML-RPC нужен Jetpack или другому сервису, не блокируйте его вслепую — используйте rate limiting или WAF и проверьте требования конкретного сервиса.
Уровень 5. IP-allowlist для административного доступа
Для закрытых административных сайтов — отличный вариант: вход разрешён только с ваших адресов, остальным — 403. Для публичных сайтов с удалёнными коллегами — риск заблокировать авторов.
Apache 2.4 (.htaccess):
<Files "wp-login.php">
Require ip 203.0.113.10
Require ip 203.0.113.11
</Files>
Подставляйте конкретные IP, а не целые подсети. Диапазон /16 можно разрешить, только если вы точно знаете, что он целиком ваш — например, статичный адрес офиса. С домашнего динамического IP вы сами не сможете войти: используйте VPN с постоянным адресом.
Nginx — добавьте allow/deny в существующий location для wp-login.php, не создавайте этот блок поверх существующей PHP-конфигурации:
location = /wp-login.php {
allow 203.0.113.10;
deny all;
}
REST API: ограничить анонимный доступ, а не закрывать
Полностью отключать REST API обычно не следует: WordPress Admin зависит от него, а отключение может сломать редактор Gutenberg и функциональность плагинов. WordPress рекомендует вместо этого при необходимости требовать аутентификацию для анонимных запросов — но только если это не ломает публичную функциональность сайта.
Пример в functions.php темы:
add_filter('rest_authentication_errors', function ($result) {
if (true === $result || is_wp_error($result)) {
return $result;
}
if (!is_user_logged_in()) {
return new WP_Error(
'rest_not_logged_in',
'Анонимный доступ к REST API закрыт',
['status' => 401]
);
}
return $result;
});
Такой подход описан в документации WordPress. Но проверьте совместимость до включения: тема, плагины, мобильные приложения и внешние интеграции могут ходить в API анонимно. На любом сайте отключение REST-доступа для анонимов — шаг, требующий теста, а не установки по инструкции.
REST API в некоторых конфигурациях может раскрывать информацию о пользователях сайта. Атакующий может собрать имена и идентификаторы пользователей, чтобы точнее подбирать логины при переборе.
После включения ограничения откройте URL вида ваш-сайт.ru/wp-json/wp/v2/users в режиме инкогнито — должен вернуться 401. Затем проверьте админку и публичные страницы: ничего не должно сломаться.
Переименование wp-login.php: помогает частично
Переименование URL входа может уменьшить количество автоматических запросов к стандартному wp-login.php: простые сканеры ищут именно этот путь. Но это не полноценная защита: целевая атака найдёт реальный URL в исходном коде темы или в аналитике. Расценивайте переименование как снижение шума, а не как заслон — защита держится на 2FA, rate limiting и WAF.
Делается плагинами вроде WPS Hide Login или iThemes Security. После переименования обновите закладки и проверьте, что плагин ограничения попыток видит новый URL входа.
Проверка результата
- Слишком много попыток — введите неверный пароль несколько раз подряд: после лимита плагин должен вернуть отказ и заблокировать IP на указанный срок.
- 2FA — выйти из админки и войти с кодом из приложения: код должен запрашиваться после пароля.
- HTTP-ответы — xmlrpc.php должен возвращать 403, а REST API для анонимов — 401.
- Рост нагрузки — сравните количество запросов к wp-login.php и нагрузку на PHP до и после настройки. После включения серверного ограничения поток попыток должен заметно сократиться.
- Access-лог — по строкам лога видно, какие запросы отсечены на уровне сервера, а какие дошли до PHP: уточняйте по конфигурации и счётчикам Nginx/Cloudflare, а не по одному виду строки лога.
Когда защита не сработает
- Распределённый ботнет — тысячи IP обходят лимиты по адресам. Для распределённого ботнета используют WAF, Challenge или CAPTCHA (Cloudflare Turnstile, Яндекс SmartCaptcha) и 2FA: они снижают эффективность автоматизации, а без кода 2FA перебор всё равно не даст войти.
- Креды уже украдены ранее — если пароль утёк из другого сервиса или сайт уже заражён, превентивная защита от перебора не поможет: нужна чистка сайта — см. что делать при взломе WordPress. После чистки — включить уровни из этой статьи.
- Атака через другие формы авторизации — кроме обычного входа через wp-login.php, атакующие могут обращаться к восстановлению пароля (wp-login.php?action=lostpassword) и другим формам авторизации. Если на сайте есть WooCommerce, кастомные формы входа или сторонние механизмы авторизации, отдельно проверьте, распространяется ли выбранный механизм ограничения на эти точки.
- Уязвимость в плагине или теме — брутфорс — не единственный способ проникновения. Эксплуатируемая уязвимость обходит все лимиты входа. Поэтому, помимо этой статьи, обновляйте ядро, плагины и темы — отдельная тема, но без неё защита неполна.
Частые вопросы
Уникальный пароль + 2FA, плагин лимита попыток, отключённый XML-RPC. На VPS добавьте Nginx limit_req, за Cloudflare — Custom Rule на страницу входа. Конкретные пороги подберите под свой сайт — проверки выше покажут, работает ли связка.
Нужна настройка защиты WordPress?
Включу 2FA, лимиты попыток входа, серверный rate limiting и WAF-правила под ваш хостинг. Диагностика бесплатно — напишите, покажу настройки вашего сайта и что закрыть в первую очередь.