このサーバをnginxに移行するまでは、WordPressでBrute Force Login Protection(BFLP)というプラグインを使っていた。これはログイン失敗の履歴を取っていって、一定以上に達すると.htaccessにdenyと書いてくれるというすぐれもの。WordPressは攻撃の標的になりやすい運命を持っているため、こういうプラグインは割と便利に使える。PHPに入る前にApacheの設定で切ってしまうというのも良い。で、実害があるとも思わなかったため、nginxに移行したあともこのプラグインをアンインストールしなかったのです。
「実害」か…実はこれ問題があった。
nginxは.htaccessを見たりはしない。だからBFLPが検出して管理者にメールを送り、.htaccessにdeny行を追加したあとも攻撃者はひたすらログイン試行を続けることができる。このような状況で何が起こるのかというと、私のメールスプールがすごいことになるというわけだ。まじですか。
先日朝起きたらすごい量のメールが飛んできていた。慌てたけどログイン成功というログは出ていなかったのでとりあえず一日放置していたら収まったのだが、このままやり過ごすのは気持ちが良くない。
nginxの場合、limit_reqで単位時間あたりのアクセス試行数を制限できる。WordPressのログインURLはwp-login.phpだから、この機能を使って、例えば1分にログイン試行10回までに制限するには、こんな感じの設定にすることになる。
http { limit_req_zone $binary_remote_addr zone=one:10m rate=10r/m; server { : location = /path/wp-login.php { limit_req zone=one burst=5 nodelay; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_pass unix:/var/run/php-fpm.sock; } } }
これでBFLPの設定でログイン試行回数10回で制限をかけるようにしていると仮定すると、あなたが受け取るメールの量は1分につき1通、一日あたりたったの1440通にまで減らせる。しかし私としてはこれではあんまり芸がないような気がした。だって、見て分かる通り(↓)、BFLPのdeny対象はもっと広い。
# BEGIN Brute Force Login Protection <FilesMatch ".*\.(php|html?|css|js|jpe?g|png|gif)$"> deny from xxx.xxx.xxx.xxx; </FilesMatch> # END Brute Force Login Protection
BFLPはスクリプトどころか、画像ファイルやCSSのダウンロードすら許さないのだ! 私としても、ウクライナの奴ら(←今回のアクセス元IPアドレス)に関してはserver全体でdenyしてしまいたい。ログインURLだけ制限するなんて生ぬるいと思えるわけだ。
そこで、incronを使って.htaccessファイルを監視し、変更があったらnginxの設定を更新してリロードをかければ良いではないか、と思った。つまり、こうだ。
# nginxの設定 http { server { : include /etc/nginx/bflp.conf; } }
そして、bflp.confはこんな感じのシェルスクリプトで生成する。
#! /bin/sh # Usage: $0 from to from=$1 to=$2 [ -r ${from} ] || exit 1 [ -w ${to} ] || exit 1 awk '/# BEGIN Brute Force Login Protection/{flag=1;}/# END Brute Force Login Protection/{flag=0;}(flag==1 && /deny from/){print "deny",$3";";}' ${from} > ${to} #sudo nginx -s reload sudo systemctl reload nginx
あとはincronでIN_CLOSE_WRITEイベント発生時にこのスクリプトが叩かれるようにすればいいだろう。
# incrontab -l /path/to/wordpress/.htaccess IN_CLOSE_WRITE /path/to/update-bflp.sh /path/to/wordpress/.htaccess /etc/nginx/bflp.conf
このスクリプトをrootで実行するのは気がひけるから、/etc/nginx/bflp.confをユーザwritableにして、一般ユーザのincrontabに登録する。ただしnginxのreloadはrootになる必要があるから、sudoのrequirettyをオフにしてNOPASSWDにしとくのを忘れずに。
ここまで設定したら、BFLPの設定画面からblacklistをいじりながら、/etc/nginx/bflp.confの動きやsystemctl status incrondやsystemctl status nginxでリロードが来ているかどうかを確認しよう。
それから、意外にやりがちなのが、incrondを初めて入れた時に起動し忘れたり自動起動にするのを忘れたり、といったところ。
これでしばらく様子を見ようと思っている。