Предотвращаем брутфорс-атаки в WordPress с помощью Fail2Ban
Fail2Ban – это простой плагин, который задает заголовок состояния 401, возвращаемый в том случае, если посетитель ввел некорректные учетные данные при запросе записи.
Код плагина умещается в несколько строк:
/* Plugin Name: Fail2Ban filter Version: 1.0 Description: Sets a 401 Status Code which shows in access logs for use with fail2ban Author: Tim Nash Author URI: https://timnash.co.uk Code Modified from Konstantin Kovshenin original - http://kovshenin.com/2014/fail2ban-wordpress-nginx/ */ function fail2ban_login_failed_401() { status_header( 401 ); } add_action( 'wp_login_failed', 'fail2ban_login_failed_401' );
Заголовок 401 записывается в мои логи доступа наряду с другой информацией о посетителе – это стандартный лог доступа и стандартное поведение для большинства серверов. Поскольку логи доступа представляют собой обычные текстовые файлы, они могут быть легко отслежены через разные приложения, и одно из таких приложений – это fail2ban. Некоторые пользователи спрашивали меня о том, как установить Fail2ban для работы с плагинами, поэтому я решил создать короткое видео-руководство вместе с более детальным объяснением конфигурации ниже.
Fail2Ban и конфигурация WordPress
Fail2Ban – это утилита, которую можно найти в большинстве систем Linux (если вы используете виртуальный хостинг, вы можете узнать у своего провайдера, установлена ли она, и могут ли к ней применяться дополнительные фильтры). Она отслеживает логи и работает как простой интерфейс для серии команд, включая взаимодействие с IPTables, стандартным unix файрволом. Если утилита не установлена, вы можете сделать это самостоятельно в Ubuntu:
sudo apt-get install fail2ban
После установки ее можно легко сконфигурировать. Fail2Ban отслеживает логи и фиксирует неудачные попытки входа, используя регулярные выражения. Эти регулярные выражения хранятся в разделе фильтров /etc/fail2ban/filters.d/. По умолчанию утилита поставляется с многочисленными общими фильтрами, начиная с SSH и заканчивая конфигурацией Apache/Nginx.
Типичная конфигурация фильтра выглядит следующим образом:
# fail2ban filter configuration for WordPress Logins [Definition] failregex = .*POST.*(wp-login.php|xmlrpc.php).* 401 ignoreregex =
По существу, она состоит из двух частей – failregex и ignoreregex. failregex – то, что ищет Fail2ban; рассматривается «fail» – неудачный вход в нашем случае. regex используется для парсинга вашего лога доступа. В коде выше он проверяет заголовки состояния 401 в ответах записей, которые (ответы) были получены из wp-login.php и xmlrpc.php во время входа. Проверка не связана с путем, т.е. в теории, если у вас wp-login.php находится в подпапке, и кто-либо инициирует запрос записи, выдающий 401, это тоже будет учтено. Это, конечно, маловероятно, но стоит это помнить. В таком случае мы можем применить ignoreregex, чтобы игнорировать вызовы для определенного файла.
Если вы не разместите
ignoreregex =
Fail2ban выведет предупреждение при рестарте, поэтому, даже если у вас нет ничего, что нужно проигнорировать, просто поместите эту строку и оставьте regex пустым.
Теперь, когда фильтр на месте, мы можем задать конфигурацию бана, которая находится в /etc/fail2ban.jail.conf. Это довольно большой файл, обладающий комментариями; я добавлю свои уникальные опции конфигурации в самый его конец:
# Jail for unauthorised WordPress login attempts # If you are using APACHE or multiple access logs change as appropriate [wordpress] enabled = true port = http,https filter = wordpress-auth logpath = /var/log/nginx/access.log maxretry = 3 bantime = 3600
- Enabled – необходимо включить, чтобы все пошло, как нам нужно
- Port – задает то, какие порты надо отслеживать (можно указывать либо число, либо портовую службу). В данном случае я определил HTTP и HTTPS, хотя мы могли бы задать 80,443.
- Filter – фильтр, созданный нами ранее – в данном случае я создал фильтр wordpress-auth.
- Logpath – путь к логам, которые нужно отслеживать. Мы отслеживаем стандартные Nginx логи, однако это может быть также и отдельный файл доступа, логи apache2, к примеру.
- Maxretry – сколько раз один и тот же IP должен присутствовать в regex результатах за определенный период времени, чтобы попасть в бан. По умолчанию это 3 раза за 600 секунд, однако вы можете изменить время, задав findtime.
- bantime – на какое время будет забанен IP (в секундах).
Как только плагин будет включен и фильтры заданы, вы можете просто перезапустить fail2ban для работы с ним. Fail2ban будет отслеживать access.log на предмет наличия неудачных логинов, занося IP в бан по мере необходимости.
Запись в syslog
В репозитории WordPress имеется плагин WP fail2ban, который работает несколько иначе, чем то, что показано в видео. Вместо использования заголовков состояния и лога доступа он просто ведет запись в syslog с помощью LOG_AUTH. То есть, если по некоторым причинам ваш лог доступа будет недоступен или запись в него будет закрыта, то заметки будут все равно оставлены (предполагая, что PHP может писать в syslog). Однако это также означает, что в вашем syslog будет много ложных записей. Если вы страдаете от распределенной брут-форс атаки, то в таком случаете ваши системные логи будут заполнены очень быстро, вследствие чего остальные ошибки будет трудно обнаружить. Однако для этого также есть многочисленные решения, и люди чаще всего не прочесывают системные логи вручную, если только они не садомазохисты.
Есть также и некоторые преимущества по ведению записи в отдельный лог или даже в syslog, особенно когда у вас запущен fail2ban на многочисленных сайтах. Сведя уведомления о неудачных входах на сайт к одной локации, вы можете отслеживать все из одного места, иначе вам придется настраивать отдельные jail-конфиги для каждого сайта.
Однако если вы работаете с одним хостом или у вас имеются централизованные логи доступа, то в таком случае использование логов доступа будет простым и легким в настройке без каких-либо дополнительных помех. Для управления логом не придется полагаться на PHP. Если у вас запущено много сайтов на разных серверах, то в таком случае вы можете рассмотреть централизацию логов; в таких ситуациях вы можете использовать инструменты, как, к примеру, monolog, для ведения отдельного централизованного лога.
Почему 401, а не 403
Мой небольшой плагин, используемый в примере, был основан на плагине Константина Ковшенина – правда, с одним небольшим изменением: он выдает 401 вместо 403. Почему? Это вопрос семантики.
- 403 – состояние Forbidden, пользователь попытался получить ресурс, к которому у него нет доступа
- 401 – состояние Unauthorised, пользователь попытался получить ресурс, однако он не предоставил корректных учетных данных для доступа к нему
Выглядят практически одинаково, однако они отличаются, и могут потенциально иметь разный вариант использования в wp-login. Если бы мы расширяли наш небольшой плагин, делали бы в нем проверку IP и блокирование аккаунта, если к аккаунту пытались получить доступ с разных IP за короткий промежуток времени, то тогда нам понадобилось бы указать разный ответ.
В этом случае:
- Мы отдаем 401, как и раньше, если ученые данные некорректные
- Мы отдаем 403, если учетные данные корректные, однако мы не открываем доступ к ресурсу из-за слишком большого числа IP-адресов.
Теперь появляется веский аргумент в пользу того, что мы не должны отдавать отдельные коды, ведь мы сообщаем злоумышленнику о том, что у него имеются корректные данные. Однако этот пример позволяет продемонстрировать потенциальные различия.
Не только баним, но и уведомляем об этом
Fail2Ban – это очень гибкая система; наряду со своей папкой filters.d, она также имеет и папку actions.d, позволяющую нам модульно применять разные действия, диапазон которых широк: начиная с отправки писем при успешном бане и заканчивая созданием своих собственных действий (как и в случае с фильтрами).
К примеру, я обнаружил, что почтовых уведомлений стало слишком много на сайтах с высоким трафиком, и не так давно я отключил sendmail на своих серверах по умолчанию. Однако я по-прежнему хочу знать, когда IP был забанен, поэтому мы можем создать действие, которое будет выглядеть примерно следующим образом, и будет использоваться для коммуникации со Slack через incoming webhook:
# Fail2Ban configuration for Slack Notification # Author Tim Nash # [Definition] # Option: actionban # Notes: Command executed when banning IP. # Values: CMD actionban = curl -X POST --data-urlencode 'payload={"channel": "#metaworld", "username": "webhookbot", "text": "Fail2Ban Reports IP has been banned by filter", "icon_emoji": ":ghost:"}' https://example.slack.com/services/hooks/incoming-webhook?token=yourtoken [Init] # Default name of the chain
Сохраняем его как slack.conf. Вы можете получить свой токен непосредственно в webhook-разделе Slack. Опять же, код напоминает наш фильтр, который мы писали ранее, однако в данном случае мы посылаем CURL запрос, который может быть заменен на shell-скрипт или что-то иное. Затем мы можем отредактировать jail.conf для добавления нашего действия:
# Jail for unauthorised WordPress login attempts # If you are using APACHE or multiple access logs change as appropriate [wordpress] enabled = true port = http,https filter = wordpress-auth action = slack[name=wordpress] logpath = /var/log/nginx/access.log maxretry = 3 bantime = 3600
Готово. Теперь, когда IP будет забанен, нам будет отправлено уведомление в Slack на наш канал metaworld , и я могу отключить почтовые уведомления в jail.conf.
Баним тех, кто уже попадался ранее
По умолчанию Fail2Ban просто временно банит IP-адреса, однако вы очень быстро столкнетесь с ботами, которые просто научатся ждать один день, и потом пробовать снова. В таких обстоятельствах вы можете заблокировать тех, кто уже попадался ранее, на постоянной основе. Я не так давно начал использовать скрипт Repeat Offender Script, который после определенного периода блокирует IP-адрес навсегда.
В итоге мы имеем простое, эффективное решение, позволяющее остановить брутфорс атаки без использования WordPress-плагинов (за исключением тех трех строк, которые были в начале). Учитывая, что брутфорс-атаки становятся все более и более значимой проблемой для владельцев WP-сайтов, очень важно помнить, что они начались задолго до появления WordPress, и у нас имеются эффективные средства для предотвращения таких атак.
Источник: timnash.co.uk