Главная / База знаний / Защита от брутфорса не только SSH: панели, CMS, формы входа
инструкция · 9 ноября 2026

Защита от брутфорса не только SSH: панели, CMS, формы входа

Как защитить от подбора паролей не только SSH, а формы входа WordPress, phpMyAdmin и собственные приложения: fail2ban-джейлы под нестандартные логи, rate limiting в nginx, CAPTCHA и риски политики блокировки аккаунтов.

SSH — не единственная точка входа для перебора паролей

Практически любой администратор, настраивавший сервер, рано или поздно ставит fail2ban для защиты SSH — эта мера стала настолько стандартной, что часто воспринимается как решение всей проблемы брутфорса разом. На деле SSH — лишь одна из множества точек входа, где кто-то может пытаться подобрать пароль: административная панель WordPress, phpMyAdmin, собственная форма входа приложения, панель управления хостингом или CMS — каждая из них представляет такую же цель для автоматизированного перебора, как и SSH, но по умолчанию не имеет никакой аналогичной защиты.

Причина этого пробела проста: fail2ban «из коробки» поставляется с готовыми джейлами под несколько самых распространённых системных сервисов (SSH, почтовые серверы, иногда nginx с базовыми паттернами), но ничего не знает о специфичном для конкретного приложения формате логов или страниц входа — эту связь администратору приходится настраивать самостоятельно, и именно это чаще всего остаётся несделанным.

Эта статья разбирает, как настроить защиту от перебора паролей для сервисов за пределами SSH: собственные джейлы fail2ban под WordPress и phpMyAdmin, rate limiting форм входа на уровне nginx, место CAPTCHA в этой цепочке защиты, риски политики блокировки аккаунтов и признаки, по которым можно распознать атаку credential stuffing в логах.

Джейл fail2ban для WordPress

WordPress без дополнительного плагина не ведёт отдельный лог неудачных попыток входа — вся информация о попытке входа остаётся в стандартном access-логе веб-сервера как обычный HTTP-запрос, из которого не всегда легко однозначно отличить успешный вход от неудачного без анализа тела ответа или редиректа.

Один из практических подходов — установить плагин, логирующий неудачные попытки входа в отдельный, предсказуемый по формату файл (многие security-плагины для WordPress имеют такую функцию), и написать под этот формат собственный фильтр fail2ban.

Фильтр fail2ban для лога плагина, фиксирующего неудачные попытки входа WordPress
# /etc/fail2ban/filter.d/wordpress-login.conf
[Definition]
failregex = ^.*Failed login for .* from <HOST>.*$
ignoreregex =

Альтернативный подход, не требующий дополнительного плагина, — фильтр по access-логу nginx, ищущий POST-запросы к wp-login.php, за которыми следует характерный для неудачного входа код ответа или повторный редирект на ту же страницу с параметром ошибки.

Джейл, блокирующий IP-адрес после 5 POST-запросов к wp-login.php за 10 минут
# /etc/fail2ban/filter.d/wordpress-nginx.conf
[Definition]
failregex = ^<HOST> .* "POST /wp-login\.php.*" 200
ignoreregex =

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

Этот подход по access-логу менее точен, чем разбор специализированного лога плагина, поскольку код ответа 200 на POST-запрос к wp-login.php не всегда однозначно означает неудачную попытку — WordPress отдаёт этот же код и при показе формы с ошибкой, и в некоторых других сценариях, поэтому фильтр стоит протестировать на реальном логе перед включением в продакшен, чтобы убедиться в отсутствии избыточных ложных срабатываний на легитимный трафик.

Джейл fail2ban для phpMyAdmin

phpMyAdmin — частая цель автоматизированного перебора, особенно на серверах, где панель осталась доступна по предсказуемому пути без дополнительной защиты паролем на уровне веб-сервера. Собственного детального лога неудачных попыток входа phpMyAdmin по умолчанию не ведёт, поэтому защита строится аналогично WordPress — через анализ access-лога веб-сервера.

Джейл для phpMyAdmin с более строгим порогом — 4 попытки за 5 минут и более длительный бан
# /etc/fail2ban/filter.d/phpmyadmin.conf
[Definition]
failregex = ^<HOST> .* "POST /phpmyadmin/index\.php.*" 200
ignoreregex =

# /etc/fail2ban/jail.d/phpmyadmin.conf
[phpmyadmin]
enabled = true
port = http,https
filter = phpmyadmin
logpath = /var/log/nginx/access.log
maxretry = 4
findtime = 300
bantime = 7200

Более надёжная и рекомендуемая мера для phpMyAdmin — не полагаться только на fail2ban, а вообще ограничить доступ к панели на уровне nginx через Basic Auth или белый список IP-адресов, если круг людей, которым нужен доступ, ограничен и предсказуем; fail2ban в этом случае становится дополнительным, а не единственным рубежом защиты.

Rate limiting форм входа на уровне nginx

Rate limiting в nginx решает похожую задачу с другой стороны — не блокирует IP-адрес полностью после превышения порога неудачных попыток, а ограничивает саму частоту запросов к чувствительному эндпоинту заранее, до того как перебор наберёт скорость, вне зависимости от того, успешна попытка входа или нет.

Ограничение частоты запросов к wp-login.php: не более 5 запросов в минуту с одного IP с небольшим допустимым всплеском
# В http-блоке nginx.conf
limit_req_zone $binary_remote_addr zone=login_limit:10m rate=5r/m;

# В server-блоке, location для формы входа
location = /wp-login.php {
    limit_req zone=login_limit burst=3 nodelay;
    try_files $uri =404;
    fastcgi_pass unix:/run/php/php-fpm.sock;
    include fastcgi_params;
}

Директива limit_req_zone определяет зону памяти и лимит скорости в привязке к ключу — в данном случае к IP-адресу клиента ($binary_remote_addr) — а параметр burst задаёт, сколько запросов сверх базового лимита допускается обработать без немедленного отказа, что нужно, чтобы не блокировать легитимного пользователя, случайно дважды подряд быстро отправившего форму (например, из-за опечатки в пароле).

Разница между этим подходом и fail2ban принципиальна: rate limiting в nginx работает превентивно и стабильно ограничивает скорость запросов независимо от их результата, реагируя мгновенно на уровне веб-сервера без необходимости парсить логи постфактум, тогда как fail2ban реагирует только после того, как накопится достаточно неудачных попыток по логам, то есть с некоторой задержкой. На практике оба механизма имеет смысл использовать вместе — rate limiting как первый, немедленный барьер, fail2ban как более долгосрочная блокировка настойчивых нарушителей.

Защита формы входа собственного приложения

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

Пример структурированной строки лога, специально спроектированной для последующего разбора fail2ban
# Пример строки лога приложения при неудачном входе
[2026-11-09 14:22:03] auth.warning: Failed login attempt for user=admin from ip=203.0.113.42
Фильтр fail2ban, точно соответствующий формату собственного лога приложения
# /etc/fail2ban/filter.d/myapp-login.conf
[Definition]
failregex = ^.*Failed login attempt for user=\S+ from ip=<HOST>$
ignoreregex =

Помимо блокировки по IP через fail2ban, на уровне самого приложения стоит реализовать прогрессивную задержку ответа при повторных неудачных попытках входа с одного и того же IP или для одного и того же логина — каждая следующая неудачная попытка обрабатывается с растущей паузой перед ответом, что делает автоматизированный перебор экономически невыгодным без явной блокировки, которая могла бы задеть легитимного пользователя, просто ошибившегося при вводе пароля.

Важно реализовать такую задержку асинхронно по отношению к обработке остальных запросов сервера, а не простым синхронным sleep внутри обработчика — при простой реализации через sleep большое количество одновременных неудачных попыток входа само способно исчерпать пул доступных воркеров веб-сервера и превратиться в непреднамеренный отказ в обслуживании для легитимных пользователей, что сводит на нет саму цель этой меры защиты.

CAPTCHA как последний рубеж, а не первая мера

CAPTCHA эффективно останавливает простые автоматизированные скрипты, не рассчитанные на её прохождение, но имеет реальную цену — она усложняет вход абсолютно всем пользователям, включая легитимных, снижает конверсию форм и раздражает людей с ограниченными возможностями или медленным интернет-соединением, для которых прохождение CAPTCHA может занимать заметно больше времени и усилий.

Разумная последовательность мер — начинать с менее навязчивых механизмов (rate limiting, fail2ban, прогрессивная задержка) и добавлять CAPTCHA только тогда, когда эти меры не справляются с объёмом или изощрённостью атак, например, когда атакующий явно распределяет запросы по множеству разных IP-адресов, обходя ограничения по одному адресу. CAPTCHA в этом случае логично показывать не всем пользователям постоянно, а адаптивно — только после нескольких неудачных попыток входа подряд, оставляя первый вход обычным для подавляющего большинства легитимных пользователей, вводящих правильный пароль с первого раза.

Современные варианты CAPTCHA, оценивающие поведенческие сигналы в фоне без явного визуального теста для большинства пользователей, заметно снижают этот компромисс по сравнению с классическими текстовыми или графическими головоломками, но полностью не устраняют его — они всё равно добавляют внешнюю зависимость и сторонний скрипт на страницу входа, что стоит учитывать при выборе.

Важно и то, что современные автоматизированные системы обхода CAPTCHA, включая распознавание через специализированные сервисы с участием живых людей, постепенно снижают эффективность CAPTCHA как самостоятельного барьера против целенаправленной, финансово мотивированной атаки — против массового, неквалифицированного скрипта она по-прежнему работает хорошо, но не стоит рассчитывать на неё как на единственную и достаточную меру защиты от организованного злоумышленника, готового заплатить за обход конкретно этой преграды.

Риск блокировки аккаунта: DoS против конкретного пользователя

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

Механизм атаки прост — если логин жертвы известен или предсказуем (например, email, который легко узнать, или стандартное имя администратора), злоумышленнику даже не нужно пытаться угадать реальный пароль: достаточно намеренно вводить заведомо неверный пароль нужное число раз, чтобы политика блокировки сама заблокировала легитимному пользователю доступ к его же аккаунту, причём повторяя это снова и снова после каждой разблокировки.

Более сбалансированный подход — блокировать не саму учётную запись полностью, а привязывать растущую задержку или временное ограничение к комбинации логина и IP-адреса, откуда идёт перебор, оставляя возможность входа с других, легитимных адресов, либо требовать дополнительное подтверждение (код на email или push-уведомление) вместо полной блокировки — это защищает от массового автоматизированного перебора, не создавая при этом инструмент для целенаправленного отказа в обслуживании против конкретной жертвы.

Распознавание credential stuffing в логах

Credential stuffing отличается от классического брутфорса по своей природе: вместо перебора паролей к одному известному логину атакующий использует готовые пары логин-пароль, скомпрометированные при утечке данных другого, никак не связанного с этим сайтом сервиса, в расчёте на статистически значимую долю пользователей, переиспользующих одинаковые пароли на разных сайтах.

В логах это проявляется характерным паттерном, отличным от обычного брутфорса: широкий разброс различных значений логина в попытках входа (а не многократные попытки к одному и тому же логину), относительно короткий промежуток времени между попытками и, что особенно важно, аномально высокая доля успешных входов среди общего объёма атаки по сравнению с фоновым уровнем — успех credential stuffing напрямую зависит от того, сколько пользователей реально переиспользуют пароли, и даже небольшой процент успеха при большом объёме проверенных пар даёт атакующему рабочие учётные записи.

Проверка на концентрацию запросов с малого числа IP и аномальный список логинов, для которых зафиксированы успешные входы за короткий период
grep "POST /login" access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10
grep "login_success" app.log | awk '{print $NF}' | sort | uniq -c | sort -rn | head -20

Обнаружение credential stuffing требует смотреть не только на неудачные попытки, но и на распределение успешных входов во времени — резкий, необычный для конкретного проекта всплеск успешных входов из разных, ранее не встречавшихся источников в короткий промежуток времени — веский повод инициировать принудительный сброс паролей для затронутых учётных записей и, если это ещё не сделано, включить двухфакторную аутентификацию как основную защиту от этого класса атак, поскольку он обходит любую защиту, построенную только вокруг качества самого пароля.

Мониторинг и алертинг по паттернам атак на формы входа

Фильтры и джейлы, разобранные выше, реагируют реактивно — блокируют конкретный IP после того, как он уже совершил заданное число подозрительных запросов. Дополняющий, более проактивный слой защиты — постоянный мониторинг агрегированных метрик по попыткам входа, позволяющий заметить аномалию в масштабе, который отдельный джейл fail2ban не увидит, поскольку каждый джейл оценивает активность одного конкретного IP-адреса изолированно.

Простой способ считать частоту попыток входа за последнюю минуту для передачи во внешнюю систему мониторинга
# Пример метрики для Prometheus/Grafana: количество неудачных входов в минуту
grep "POST /wp-login.php" /var/log/nginx/access.log | \
  awk -v d="$(date --date="-1 min" +[%d/%b/%Y:%H:%M)" '$0 ~ d {c++} END{print c+0}'

Резкий рост общего числа попыток входа за короткий промежуток времени, даже если каждая отдельная попытка идёт с нового, ранее не встречавшегося IP-адреса и потому не достигает порога блокировки ни одного отдельного джейла, — характерный признак распределённой атаки, которую стоит выносить в отдельный алерт системы мониторинга, а не полагаться на то, что её поймает точечная защита каждого отдельного механизма.

Практическая рекомендация — настроить оповещение (в Grafana, Zabbix или аналогичной системе) на превышение общего количества попыток входа за скользящее окно времени сверх обычного для проекта фонового уровня, независимо от того, с скольких разных IP эти попытки распределены — такой алерт даёт команде время отреагировать вручную (например, временно включить более строгий rate limiting или CAPTCHA) ещё до того, как атака успеет привести к реальной компрометации хотя бы одной учётной записи.

Определять «обычный фоновый уровень» стоит на основе исторических данных конкретного проекта, а не универсального значения из чужой статьи — небольшой корпоративный сайт с редкими входами администраторов и крупный интернет-магазин с тысячами ежедневных входов пользователей имеют принципиально разный нормальный фон, и порог алерта, скопированный без учёта этой разницы, либо будет постоянно ложно срабатывать, либо пропустит реальную атаку, которая для конкретного проекта уже аномальна, но остаётся ниже общего, не подстроенного под контекст порога.

почему мы

Почему стоит выбрать нас

Быстрый запуск

Подберём конфигурацию и развернём проект в день обращения.

Надёжность

NVMe-диски, защита от DDoS и резервные копии по расписанию.

Поддержка 24/7

Дежурные инженеры на связи в любое время суток.

Настройка под ключ

Установим CMS, настроим веб-сервер, SSL и почту за вас.

Перенос без простоя

Аккуратно перенесём сайт и базу данных на новый сервер.

Прозрачные цены

Актуальные тарифы и честный подбор без навязывания.

частые вопросы

Вопросы и ответы

Достаточно ли настроить fail2ban только для SSH?

Нет, если на сервере работают другие сервисы с формами входа — WordPress, phpMyAdmin, собственная административная панель — они точно так же остаются целью автоматизированного перебора паролей, и без отдельного джейла под каждый из них fail2ban просто не увидит и не заблокирует эту активность.

Как fail2ban понимает, что кто-то подбирает пароль к WordPress, если это не системный сервис?

Через собственный лог приложения или через логи веб-сервера — WordPress без специального плагина не пишет отдельный лог неудачных попыток входа, поэтому либо ставится плагин, логирующий неудачные попытки в отдельный файл, либо fail2ban настраивается на разбор паттерна POST-запросов к wp-login.php в access-логе nginx с кодом ответа, характерным для неудачного входа.

Не лучше ли просто заблокировать доступ к wp-login.php по IP через nginx?

Это хорошее дополнительное решение для случаев, когда админку использует ограниченный круг известных IP-адресов, но не универсальное — если редакторы или администраторы сайта заходят с разных, непредсказуемых адресов, жёсткая блокировка по списку IP невозможна, и здесь rate limiting или fail2ban оказываются более гибкой альтернативой.

В чём риск политики блокировки аккаунта после нескольких неудачных попыток входа?

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

Что такое credential stuffing и чем он отличается от обычного брутфорса?

Credential stuffing — это попытка входа парами логин-пароль, украденными из утечки данных другого, не связанного с этим сайтом, сервиса, в расчёте на то, что часть пользователей переиспользует одинаковые пароли — в отличие от классического брутфорса, перебирающего пароли к известному логину, credential stuffing использует уже готовые, статистически рабочие пары для множества разных логинов сразу.

Стоит ли ставить CAPTCHA на форму входа сразу, не дожидаясь проблем?

Не обязательно — CAPTCHA ухудшает пользовательский опыт для всех легитимных пользователей ради защиты от автоматизированных атак, поэтому разумнее сначала использовать менее навязчивые меры (rate limiting, fail2ban) и вводить CAPTCHA как последний рубеж только тогда, когда более лёгкие меры не справляются с объёмом атак.

Работает ли rate limiting в nginx против распределённого брутфорса с разных IP-адресов?

Ограниченно — rate limiting по умолчанию считает запросы в привязке к IP-адресу клиента, поэтому атака, распределённая по большому числу разных адресов (ботнет), обходит лимит на каждый отдельный адрес; для такой ситуации нужны дополнительные меры уровня приложения — например, анализ паттерна запросов или временная блокировка конкретного атакуемого логина независимо от IP.

Как отличить настоящую атаку credential stuffing в логах от обычного трафика?

Характерные признаки — резкий всплеск POST-запросов к странице входа за короткий промежуток времени, широкий разброс различных значений логина при относительно небольшом числе исходных IP-адресов (или наоборот, огромное число разных IP с похожим паттерном запросов), а также нехарактерно высокая доля неудачных попыток входа по сравнению с обычным фоновым уровнем.

Как оплачивать услуги?

Оплата помесячная, для облачных серверов доступна почасовая тарификация. Принимаются карты, банковские переводы и электронные кошельки.

Есть ли круглосуточная поддержка?

Да, дежурные инженеры на связи 24/7.

Выдаёте ли закрывающие документы?

Да, предоставим необходимые документы для оплаты.

Как оставить заявку?

Заполните форму на странице или нажмите «Заказать» — мы свяжемся и поможем с подбором.

заявка

Оставьте заявку

Ответим и поможем с подбором и настройкой.