Защита сайтов на WordPress с помощью Fail2Ban

Ваши сайты на WordPress пытаются ломать?

Мой VDS находится под мониторингом Zabbix’а. Вчера я стал активно получать на почту сообщения о большой загрузке сервера.

Я не выдержал и зашел на Zabbix посмотреть графики загрузки.

CPU Utilization

CPU Utilization

CPU Load

CPU Load

То, что я увидел, меня немножко поразило. Идёт постоянная, 100%
загрузка CPU пользовательскими процессами (первый график, синий цвет) и
резко вырос CPU Load (второй график).

Начиная с 18:00, я поборол попытки взлома и загрузка процессора упала до нормального уровня.

Использовав команду top, я обнаружил, что нагрузку создает процесс httpd
(Apache). Это было ожидаемо, больше на сервере ничего нет, но я не
предполагал, что мои скромные сайты, с малой посещаемостью так могут
нагрузить сервер. Тут закрались сомнения…

Я полез в логи Apache, запустил Server-Status и увидел, что на одном из моих виртуальных хостов, с различных американских IP, каждую секунду долбятся на страницу авторизации WordPress.

Apache access log file ... 142.54.188.122 - - [25/Jun/2017:03:07:12 +0300] "GET /wp-login.php HTTP/1.0" 200 12665 "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)" 142.54.188.122 - - [25/Jun/2017:03:07:13 +0300] "POST /wp-login.php HTTP/1.0" 200 12991 "http://4ham.ru/wp-login.php" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)" 142.54.188.122 - - [25/Jun/2017:03:07:15 +0300] "POST /xmlrpc.php HTTP/1.1" 403 212 "-" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)" 142.54.188.122 - - [25/Jun/2017:03:07:15 +0300] "GET /wp-login.php HTTP/1.0" 200 12666 "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)" 142.54.188.122 - - [25/Jun/2017:03:07:16 +0300] "POST /wp-login.php HTTP/1.0" 200 12991 "http://4ham.ru/wp-login.php" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)" 142.54.188.122 - - [25/Jun/2017:03:07:18 +0300] "POST /xmlrpc.php HTTP/1.1" 403 212 "-" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)" 142.54.188.122 - - [25/Jun/2017:03:07:18 +0300] "GET /wp-login.php HTTP/1.0" 200 12667 "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)" 142.54.188.122 - - [25/Jun/2017:03:07:19 +0300] "POST /wp-login.php HTTP/1.0" 200 12990 "http://4ham.ru/wp-login.php" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)" 142.54.188.122 - - [25/Jun/2017:03:07:21 +0300] "POST /xmlrpc.php HTTP/1.1" 403 212 "-" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)" 142.54.188.122 - - [25/Jun/2017:03:07:21 +0300] "GET /wp-login.php HTTP/1.0" 200 12667 "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)" 142.54.188.122 - - [25/Jun/2017:03:07:22 +0300] "POST /wp-login.php HTTP/1.0" 200 12991 "http://4ham.ru/wp-login.php" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)" 142.54.188.122 - - [25/Jun/2017:03:07:24 +0300] "POST /xmlrpc.php HTTP/1.1" 403 212 "-" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)" ... 

Тут приведен фрагмент только с одним IP адресом, на самом деле их больше.

С помощью команды whois я узнал кому принадлежит IP адрес атакующего (на тот момент, ещё предполагаемого атакующего, т.к. я ещё точно не знал, что он делает и какая его цель).

whois 142.54.188.122 | grep Country
Country         US

Как видим выше данный IP адрес принадлежит сети расположенной в США.

Я не особо боялся, что меня взломают. Со стороны WordPress стоят
различные средства защиты от взлома (плагины Limit Attempts и Google
Captcha (reCAPTCHA)). Они прекрасно защищают, но никак не помогают
снизить нагрузку на сервер когда происходит массированная атака. Атаку
отбивает сам WordPress, при попытке подбора пароля (брутфорс) он
блокирует IP атакующего добавляя его в чёрный список доступа к сайту,
т.е. выполняет функцию схожую с Firewall, но при этом потребляет уйму
ресурсов.

Нужно защищать на более низком, сетевом уровне. Я решил применить
fail2ban в связке с iptables, который уже установлен и защищает SSH, а
теперь пришло время защищать и Apache.

Приступим к настройке

Необходимо, чтобы WordPress писал в лог файл информацию о неудачной
попытке аутентификации. В дальнейшем, эту информацию будет использовать
Fail2ban для добавления IP адреса злоумышленника в iptables (firewall).

Установка плагина для WordPress

Устанавливаем и активируем в WordPress плагин WP fail2ban. Он как раз и занимается тем, что пишет в лог файл сообщения о не удачных аутентификации.

В какой лог файл будет писаться сообщения зависит от настройки syslog. В Ubuntu плагин пишет в /var/log/auth.log, Centos /var/log/messages.

Установив плагин в логах я сразу увидел множество подобных записей:

Sep 20 13:11:24 server wordpress(obu4alka.ru)[30077]: Authentication attempt for unknown user admin from 223.204.235.194

Теперь стало видно, что атакующие пытаются подобрать имя пользователя (admin) для доступа к сайту и подобрать пароль к существующему пользователю (UserLogin) на сайте.

Установка и настройка fail2ban

Если fail2ban у вас не установлен, то устанавливаем

sudo apt install fail2ban

и приступаем к настройке.

Благодаря плагину WP fail2ban, нам известны IP адреса атакующих, теперь необходимо корректно настроить фильтр для fail2ban.

В комплекте с плагином WP fail2ban идёт готовый фильтр, но он какой-то недоделанный. Он не анализирует строку с неверной аутентификацией существующего пользователя:

Sep 20 13:11:24 server wordpress(obu4alka.ru)[30077]: Authentication attempt for unknown user admin from 223.204.235.194

а это то, ради чего всё и затевалось! Слегка подредактируем фильтр под наши задачи.

Копируем фильтр из подкаталога плагина в подкаталог фильтров fail2ban:

cp wp-content/plugins/wp-fail2ban/filters.d/wordpress-hard.conf /etc/fail2ban/filter.d 

Добавляем в фильтр дополнительное регулярно выражение: 

^%(__prefix_line)sAuthentication failure for .* from <HOST>$

Мой фильтр выглядит следующим образом:

sudo nano /etc/fail2ban/filter.d/wordpress-hard.conf

# Fail2Ban filter for WordPress hard failures 
#
[INCLUDES]
 before = common.conf
 [Definition]
 _daemon = (?:wordpress|wp)
 failregex = ^%(__prefix_line)sAuthentication attempt for unknown user .* from $
             ^%(__prefix_line)sREST authentication attempt for unknown user .* from $
             ^%(__prefix_line)sXML-RPC authentication attempt for unknown user .* from $
             ^%(__prefix_line)sSpam comment d+ from $
             ^%(__prefix_line)sBlocked user enumeration attempt from $
             ^%(__prefix_line)sBlocked authentication attempt for .* from $
             ^%(__prefix_line)sXML-RPC multicall authentication failure from $
             ^%(__prefix_line)sPingback error .* generated from $
             ^%(__prefix_line)sAuthentication failure for .* from $
 ignoreregex =

Настраиваем конфигурационный файл jail.conf

sudo nano /etc/fail2ban/jail.conf 
enabled = true 
filter = wordpress-hard 
logpath = /var/log/auth.log 
action  = iptables-multiport[name=NGINX,port="http,https",protocol=tcp]
sendmail-whois[name=WordPress, dest=your@email.ru] 
maxretry = 3 
findtime = 7200 
# bantime: 1 year 
bantime = 31536000 
  • port — блокируем сразу для двух портов 80 и 443;
  • dest — email на который будут приходить отчёты о бане IP;
  • maxretry — после какого количества неудачных попыток аутентификации, будет забанен IP;
  • findtime — время в секундах, в течение которого учитывается maxretry
    (сейчас у ботов пошла новая стратегия по подбору паролей, они делают
    две попытки, потом ждут час и опять две попытки, так что лучше это
    параметр установить минимум на два часа [08/11/2018]);
  • bantime  — на сколько секунд банить IP (в моём случае на 1 год).

Перезапускаем fail2ban:

sudo systemctl restart fail2ban 

Мониторинг работы fail2ban

Настройка закончена, теперь посмотрим, что происходит с атаками на нас.

Смотрим лог fail2ban:

tail /var/log/fail2ban.log

2019-09-20 13:18:06,630 fail2ban.actions        [30822]: NOTICE  [wordpress-hard] Restore Ban 96.44.134.26
 2019-09-20 13:18:06,667 fail2ban.actions        [30822]: NOTICE  [wordpress-hard] Restore Ban 96.44.134.28
 2019-09-20 13:18:06,705 fail2ban.actions        [30822]: NOTICE  [wordpress-hard] Restore Ban 96.47.231.222
 2019-09-20 13:18:06,744 fail2ban.actions        [30822]: NOTICE  [wordpress-hard] Restore Ban 96.47.231.28
 2019-09-20 13:18:06,780 fail2ban.actions        [30822]: NOTICE  [wordpress-hard] Restore Ban 96.47.231.30
 2019-09-20 13:18:06,819 fail2ban.actions        [30822]: NOTICE  [wordpress-hard] Restore Ban 96.47.231.53
 2019-09-20 13:18:06,857 fail2ban.actions        [30822]: NOTICE  [wordpress-hard] Restore Ban 96.9.87.54
 2019-09-20 13:18:06,894 fail2ban.actions        [30822]: NOTICE  [wordpress-hard] Restore Ban 97.74.232.143
 2019-09-20 13:18:06,932 fail2ban.actions        [30822]: NOTICE  [wordpress-hard] Restore Ban 97.74.232.80
 2019-09-20 13:18:06,971 fail2ban.actions        [30822]: NOTICE  [wordpress-hard] Restore Ban 97.74.4.114

Видим, что fail2ban подгрузил наш фильтр wordpress-hard, сразу увидел попытки взлома и при превышении количества попыток банит (блокирует) IP.

Смотрим какие IP уже забанены:

sudo iptables-save

-A f2b-NGINX -s 103.216.72.154/32 -j REJECT --reject-with icmp-port-unreachable
 -A f2b-NGINX -s 103.215.139.38/32 -j REJECT --reject-with icmp-port-unreachable
 -A f2b-NGINX -s 103.21.180.40/32 -j REJECT --reject-with icmp-port-unreachable
 -A f2b-NGINX -s 103.209.144.117/32 -j REJECT --reject-with icmp-port-unreachable
 -A f2b-NGINX -s 103.209.1.69/32 -j REJECT --reject-with icmp-port-unreachable
 -A f2b-NGINX -s 103.208.137.238/32 -j REJECT --reject-with icmp-port-unreachable
 -A f2b-NGINX -s 103.206.245.67/32 -j REJECT --reject-with icmp-port-unreachable
 -A f2b-NGINX -s 103.198.69.92/32 -j REJECT --reject-with icmp-port-unreachable
 -A f2b-NGINX -s 103.198.52.1/32 -j REJECT --reject-with icmp-port-unreachable
 -A f2b-NGINX -s 103.197.48.178/32 -j REJECT --reject-with icmp-port-unreachable
 -A f2b-NGINX -s 103.18.56.35/32 -j REJECT --reject-with icmp-port-unreachable
 -A f2b-NGINX -s 103.16.199.56/32 -j REJECT --reject-with icmp-port-unreachable
 -A f2b-NGINX -s 103.15.226.14/32 -j REJECT --reject-with icmp-port-unreachable
 -A f2b-NGINX -s 103.14.124.72/32 -j REJECT --reject-with icmp-port-unreachable
 -A f2b-NGINX -s 103.134.41.58/32 -j REJECT --reject-with icmp-port-unreachable

Вот они, все забаненные тут видны, и это малая часть адресов.На самом деле их гораздо больше…

Теперь у меня тишины в логах и загрузка процессора опустилась до нормальных пределов.

Загрузка CPU после установки fail2ban

Загрузка CPU после установки fail2ban

Периодически на почту приходят сообщения о новых забаненных IP.

Статистика за неделю

За неделю заблокировано всего 85  IP адресов… но эти IP адреса очень активно ломали сайт. После их блокировки, сайт стал работать значительно быстрей, т.к. снизилась нагрузка на сервер.