Изучая вчера логи Web-сервера, я заметил довольно наглую попытку подбора пароля.
80.249.65.240 — — [16/Sep/2014:17:01:46 +0700] ʺPOST /wp-login.php HTTP/1.0ʺ 200 3803 ʺ-ʺ ʺ-ʺ 224 4172
80.249.65.240 — — [16/Sep/2014:17:01:47 +0700] ʺPOST /wp-login.php HTTP/1.0ʺ 200 3803 ʺ-ʺ ʺ-ʺ 223 4172
80.249.65.240 — — [16/Sep/2014:17:01:47 +0700] ʺPOST /wp-login.php HTTP/1.0ʺ 200 3803 ʺ-ʺ ʺ-ʺ 223 4172
80.249.65.240 — — [16/Sep/2014:17:01:47 +0700] ʺPOST /wp-login.php HTTP/1.0ʺ 200 3803 ʺ-ʺ ʺ-ʺ 223 4172
Разумеется, я зверски расправился с этим ботом, забанив его IP-адрес на фаерволе, а также отправил гневное шаблонное письмо его провайдеру. Но душа явно не была довольна. Хотелось как-то пресечь такие попытки подобрать пароль на корню. Можно, конечно, установить fail2ban или написать его аналог. Можно самому скреативить нехитрый скрипт и повесить его как обработчик логов nginx. Но мне захотелось попробовать менее затратный вариант — использовать средства самого nginx.
Сделав несколько выборок из логов, я обнаружил, что за этот год было 114 тысяч попыток подбора паролей. IP-адресов было меньше сотни. Практически во всех запросах был пустой User-Agent (выделено красным). Конечно, сам по себе этот факт уже должен служить поводом для паники. Даже всякие wget-ы и curl-ы представляются, не говоря уже о браузерах, которые помимо своей версии сообщают много полезной информации и о вычислительном устройстве посетителя. Но все же я могу предположить, что какой-нибудь отъявленный маньяк прикрутил к браузеру плагин, фильтрующий User-Agent. Поэтому было принято решение выдавать ошибку 403 тем, у кого пустой User-Agent и кто использует метод POST.
В ходе реализации задуманного выяснилось, что nginx не умеет обрабатывать вложенные операторы if. Также в его арсенале нет логических и/или. Конечно, nginx — не скриптовый движок, а Web-сервер, и потому такое поведение вполне допустимо. Но неужели нельзя решить такую простую задачу — заблокировать пустой User-Agent и метод POST в одном запросе? Оказывается можно! На помощь приходит возможность определять собственные переменные. Сначала я определил в nginx.conf переменную, состоящую из метода и User-Agent, разделенных вопросительным знаком:
set $a $request_method?$http_user_agent;
Затем я проверяю содержимое этой переменной и выдаю ошибку 403 всем клиентам, у кого наблюдается сочетание метода POST и пустого User-Agent:
if ($a = «POST?») {
return 403;
}
Теперь совсем тупые боты не смогут напасть на Web-сервер.