おかげさまでblogへのスパム投稿はAkismetがそのほとんどを防いでくれているので、大いに助かっているのですが、その一方でスパム投稿を目論んでいるであろう不正アクセス(とまでは言えないか?)は相変わらず大量に押し寄せています。
そんな中、iptablesによって目に余るアクセス元は遮断してはいますが、それだけで対応するのは現実的ではありません(実際にどんだけiptablesで制限しても不正アクセスは大量に押し寄せてくる)。
というのも、例えばcounterize IIでアクセスログを表示させれば、この通り。
counterize IIではその直近50件のアクセスログを表示できますが、その大半がこういう状態になっていることもしばしば。
わかりますかね、これ。
リファラーがすべてhttps://boota.mydns.jp/blog?p=849#comment-63になってるんです。
そしてアクセスしているのはすべてhttps://boota.mydns.jp/blog?p=849。
ちなみにapacheのログを見ると、GETだけでなくほぼ例外なくPOSTも入っています(失敗していますが)。
困ったことに、ここんとこログを確認した限りではそのアクセス元は198.2.197.212。しかしこれ、whoisしても該当するネットワークが分からないんですよねー。しかも同じようなスパム投稿目的のアクセスで他にも198.2.202.156だの198.2.197.221だのと、もしIPアドレスが正しいのであればおそらく同じネットワークに属するのであろうIPアドレスからのアクセスが頻発。
第一、第二オクテットが揃っていると、163dataの時みたいにIPアドレスをひっくり返してヒットする情報が適切なのかどうか、ちょっと判断が付き兼ねます。
というわけで、今回はリファラーに注目してapacheでアクセス制限をかけてみることにしました。
参考になりそうな情報はネット上に多々あるのですが、今回は特にコチラを参考にさせていただきました。
手順としては、apacheの設定ファイル(httpsd.conf)にて、環境変数の設定とアクセス制限の設定を行うだけです。
環境変数の設定はログの振り分けで行ったSetEnvIf。アクセス制限の設定に関しては、特に細かく分けることもないかな?ということで、</var/www/html/blog>ディレクティブを新たに作成してそれに対して行うことに。
さて、まずはSetEnvIfですが、自分でSetEnvIfを記述したところに付け加えることにします。基本構造は、こんな感じ。
SetEnvIf Referer “https://boota.mydns.jp/top/top_upper.htm” ban
SetEnvIfで指定する対象は今回はUser-AgentではなくRefererになります。そしてそのRefererのアドレス(ドメイン)と、変数名の定義です。
もう一方のアクセス制限は、同じくhttpsd.confにある</var/www/html/blog>ディレクティブに記述した(デフォルトの)
Order allow,deny
Allow from all
のところに施します。例えば先ほどの環境変数をAllow from allの後に付け加えると、
Deny from env=ban
これでhttps://boota.mydns.jp/top/top_upper.htmからのアクセスは拒否されることになります。
ちなみにこの例にあるtop_upper.htmは私のwebページのインデックス(フレーム)の上段のページで、ここからblogへのリンクが貼られているのですが、じかに(例えばブックマークやアドレス直接入力)ではこのblogを表示できますが、webページのリンクをクリックするとForbiddenが表示されるようになります。
なので、うまくいくかどうかはこれまたしばらく様子見ということになるのですが、リファラの部分を先のコメント云々というアドレスに設定し、アクセス禁止とログ振り分けを行えば、apacheのログもcounterize IIのログもすっきりするのではないかと考えました。
そこで先のhttpsd.confでアクセス制限のところはそのままに、SetEnvIfを次のようにしました。
SetEnvIf Referer “https://boota.mydns.jp/blog*comment*” ban no_log
正規表現のルールに従えば、これでhttps://boota.mydns.jp/blog?p=849#comment-63やそれに類するもの(例えばhttps://boota.mydns.jp/blog/?p= 849#comment-63++++++++++++++++ ++++++Result:+chosen+nickname+ %22MumsPauluse%22;+success+%28 from+first+page%29;なんてやつもある)が排除できる・・・はずですよねぇ?(アクセスもログも)
というわけで、しばらくしてログがどのようになるか、ちょっと楽しみです(^-^)
予想したとおりの動作をしてくれると、うれしいな~。
→その後、これではうまく弾けないことがわかりました。
正規表現のルールに正しく従っていなかったからのようです(ちゃんと勉強しないとダメですね・・・)。
で、再度調べ直して、今度はこうなりました。
SetEnvIf Referer “https://boota\.mydns\.jp/blog*comment*” ban no_log
.(ピリオド)の前に\(バックスラッシュ)をつけて、.をちゃんと.として認識するようにしなくちゃいけなかったんですね(もっとも、バックスラッシュをつけていないで解説しているサイトもありますし、どっちが正しいんでしょう?)。
さぁ、これで第二回戦スタート!今度はどうかな?
※よくよく考えてみたら、アクセスを禁止し、ログをとらなくしてしまったら、アクセスを排除で来ているのかそもそもアクセス自体が(たまたま)なかったのか確認できなくなってしまいます。
そこで、環境変数banに合致するものをspam_logとしてログに記録することにしました。
こうすれば、アクセス自体があったのかどうかわかるはず・・・ですよね?(実験してみた限りでは問題なさそうですが・・・はてさて)
※※そしてその後の結果ですが、残念ながらアクセス制限(およびログの振り分け)はうまく機能しませんでした。相変わらずhttps://boota.mydns.jp/blog?p=849#comment-63からのアクセスはあるし、しかもこのアクセスはaccess_logに記録されちゃってるし・・・。
いったい、なにがだめなんだろう・・・。
※※※解決しました
なんのことはない、やっぱり私の正規表現の仕方に間違いがありました。
正規表現では、「*」はワイルドカードではなく、直前の文字の0回以上の繰り返しを意味するんです。
だから、私が書いたやり方だと、
https://boota.mydns.jp/bloggggcommentttt
みたいなパターンじゃないと引っかからないわけです。
そうではなく、https://boota.mydns.jp/blog/(任意の文字列)comment(任意の文字列)とするには(つまりワイルドカードにするには)、
任意の一文字+その任意の文字列の0回以上の繰り返し
と言うかたちで指定しなければなりません。
任意の一文字は「.」で表しますから、この場合は「.*」としなくてはいけなかったんです。
つまり正しくは
“^https://boota\.mydns\.jp/blog/.*comment.*”
にしなくてはいけなかったんですね。
(blog/の後の「.」は文字としてのピリオドではないのでバックスラッシュはない!)
ちなみに、もし数字のところだけをワイルドカード的に指定するのであれば、
“^https://boota\.mydns\.jp/blog/\?p=[0-9]*#comment-[0-9]*”
でいけるっぽいですね(試してませんが)。
?は特殊記号なのでバックスラッシュを頭につけ、ページ番号部分は0-9をワンセットにして任意の桁数分という方法で指定しています。
さぁ、今渡こそうまくいくはず!
httpsd.confを書き換えたら、httpsdをリスタートしてまたまたしばらく放置して状況確認だっ!
と書き込んだ矢先にいきなり問題発生。
上記の”^https://boota\.mydns\.jp/blog/.*comment.*”で指定してしまうと、adminページのcomment.phpにもアクセスできなくなってしまうのです。
そりゃそうですよね、だってばっちり条件に合致してしまいますもの。
そこで、数字だけをワイルドカード的に指定する方に再度変更をして動作検証を行うことになりました。
楽してワイルドカードの範囲を広くとってしまうと、予期せぬところでトラブルになるということを勉強しました・・・(笑)