Скрипт блокирования brute-force атак в .htaccess
Ваш сайт атакуют. Если не прямо сейчас, то несколько раз в день. Подбирают пароли, нащупывают уязвимости. В логе ошибок это выглядит примерно так:
[Thu Jul 13 05:28:49 2017] [error] [client 196.64.59.18] ModSecurity: Access denied with code 401 (phase 2). Operator GT matched 0 at USER:bf_block. [file "/etc/apache2/mod_security/custom/wpbrute.conf"] [line "20"] [id "9999001"] [msg "ip address blocked for 5 minutes, more than 15 login attempts in 3 minutes."] [hostname "giraffesdoexist.com"] [uri "/wp-login.php"] [unique_id "WWdngc26uBEAAGIsOAEAAABG"]
Сервер, в моём случае, Апач, справляется с этими атаками, но говноеды возвращаются снова и снова. Помимо лишней нагрузки на сервер, есть небольшой шанс, что в какой-то момент атака будет успешной, поэтому я сделал простенький скрипт, который блокирует адреса взломщиков на уровне .htaccess.
Этот скрипт запускается через cron с тремя параметрами:
blockbruteforcers.sh ../logs/error_log ../domains/.htaccess 200
В процессе выполнения он ищет в файле error_log ip-адреса, которые встречаются там более 200 раз и записывает их в .htaccess с префиксом "deny from". Вот полный текст скрипта, не забудьте поставить ему атрибут "Execute" при создании на сервере.
#!/bin/bash if [ $# -ne 3 ]; then echo "Incorrect number of arguments. Should be 3: apache error log path, .htaccess file path, number of entries." exit fi filename=$2 num_entries=$3 error_log=$1 #If .htaccess file does not exist, create it and add 2 first lines if [ ! -f "$filename" ]; then echo order allow,deny >> "$filename" echo allow from all >> "$filename" fi #Check if .htaccess file has new line at the end. If not - add it. c=`tail -c 1 "$filename"` if [ "$c" != "" ]; then echo >> "$filename" fi msg="" arr=($(grep -o 'client [0-9.]*' "$error_log"| sort -r | uniq -c | sort -nr | awk -v pNum=$num_entries '$1>pNum {print $3}')) for i in "${arr[@]}" do if ! grep -q -P "$i$" "$filename"; then if [ ! -z "$msg" ]; then msg+="\n" fi msg+="deny from ${i}" fi done if [ ! -z "$msg" ]; then echo -e "$msg" >> "$filename" echo "$msg" else echo "no matching records found" fi