WordPress でのコメントスパムとの戦い

このブログのように弱小サイトでもコメントスパムが来ます。 最盛期は 1か月に 6万7千件という記録があります。 最近は減ってきたものの、それでも管理画面にログインして「200件のコメントがスパムとして捕えられています」というようなメッセージを見るのは気分がいいものではありません。 今回、原因調査と対策をしたことでスパムキューに入るコメントの数そのものを減らすことができたので、経緯をまとめておきたいと思います。

スパムキューに入るコメントの数を減らしたい

使っている人は誰でも知っていると思いますが、WordPress には標準で Akismet というスパム対策用のプラグインがついてきます。 個人用には無償で利用でき、これはこれで有難いプラグインなのですが、日本語についてはたまに誤検知が発生するようです。 少なくとも私は正規のコメントをスパムキューに入れられた経験が複数回あります。

そういうことがあると、どうしてもスパムキューのチェックをしたくなってしまいます。 スパムと正規コメントがそれこそ 10,000 : 1 ぐらいの比で入っているようなキューをチェックするのは至難の業です。 やはり、根本的な対策を行ってスパムキューに入るコメントの数を減らしたい。

そこで私は CAPTCHA プラグインを導入してみました。

SI CAPCHA を導入したのにスパムが減らない?

私が導入したのは SI CAPTCHA プラグインです。 レビュー評価も高く、最近もメンテナンスされている様子なのでこれを選びました。

しかし、何故か導入後も思ったようにスパムキューに入るコメントの数が減らなかったのです。 そこで、この原因を調べてみました。

人間じゃないな

まずは httpd のログを確認します。 スパムコメントが投稿される際のログはたった 4行でした。

192.168.138.148 - - [27/Sep/2014:10:28:03 +0900] "GET /archives/503 HTTP/1.0" 200 60116
192.168.138.148 - - [27/Sep/2014:10:28:05 +0900] "GET /wp-content/plugins/si-captcha-for-wordpress/captcha/securimage_show.php?si_form_id=com&prefix=TdkHIF8aBTtW9BWL HTTP/1.0" 200 6099
192.168.138.148 - - [27/Sep/2014:10:28:05 +0900] "POST /wp-comments-post.php HTTP/1.0" 302 -
192.168.138.148 - - [27/Sep/2014:10:28:09 +0900] "GET /archives/503 HTTP/1.0" 200 60260

通常の Web ブラウザを使った閲覧ならば、1行目のページ全体の html がダウンロードされた後に関連する画像や JavaScript 等がダウンロードされるのですが、それは行われていません。 CAPTCHA の画像 (2行目) をダウンロード後、コメントを POST (3行目) し、再度そのページを取得 (4行目) しているだけです。

注目すべきは 2行目と 3行目の間の経過時間、すなわち CAPTCHA 画像をダウンロードしてからコメントを投稿するまでの時間です。 これをみると 1秒も経たずに行われています。

以前、この世界には安価な人力を使って CAPTCHA を突破するような仕組みが存在するという話をどこかで見かけたのですが、このログを見る限り人間が処理しているわけではないようです。 私はこの時点は 2つの可能性を考えていました。 一つはプラグインが何か脆弱性を持っていてそこを突かれている可能性、もう一つは CAPCHA 画像を機械的に認識処理している可能性です。

作者の方には申し訳ないのですが、ぼんやりと脆弱性の可能性の方が高いように考えていました。

SI CAPTCHA の仕組み

SI CAPTCHA の仕組みを簡単に説明しておきます。

ブログのページが表示されたとき、4文字の CAPTCHA 文字列 (captcha_code) がランダムに生成され、その内容は cache ディレクトリ内の一時ファイルに保存されます。 保存されたファイル名にはランダムの文字列 (si_code_com) が付与されます。 先のログの「TdkHIF8aBTtW9BWL」の部分がこの si_code_com に相当します。

CAPTCHA

コメント投稿時に POST された captcha_code と si_code_com から一時ファイルの内容と合っているかを確認し、合っていなければエラーとする仕組みです。 上の図はこれらの処理をイメージ化したものです。 この例では si_code_com = TdkHIF8aBTtW9BWL、captcha_code = 9YgA です。

画像認識処理されてるよ!

SI CAPTCHA のソースコードを読んでこれらを把握したのですが、コードを読むだけでは特に問題となりそうな箇所は見つけることができませんでした。 そこで、投稿されている内容をログに記録することにしました。 具体的には si-captcha.php 内にある関数 si_captcha_comment_post() で $_POST の値を確認するようにし、プログラムが生成時に使用している captcha_code や si_code_com と比べてみました。

すると驚いたことにスパマーは正しい captcha_code と si_code_com を送ってきていたのです。 一応付け加えておくと出鱈目な captcha_code を付けて POST してくるケースもあったのですが、それらはきちんと弾かれていました。

captcha_code と si_code_com が正しいとなるとどうやら画像認識処理をしているようです。 作者さん、疑ってごめんなさい!

キューに入る前にシャットアウト!

さて、画像認識処理が機械的に行われているとなるとどうすればよいでしょうか。 高度な画像認識処理が行われていて、認識率が結構高いとなるとやっかいです。 プラグインを変えなければならないかも知れません。

それは面倒だなあと思いつつ、まずはできるところを試してみようと思いました。 何を行ったかというと SI CAPTCHA のコードをいじって文字数と画像サイズを変えたのです。

すると、スパマーが投稿する際の captcha_code に変化が見られました。

["captcha_code"]=> string(14) "!UNKNOWN_TYPE!"

やったね!!

これだけで Unknown type とか言っちゃう程度の処理エンジンであればこの対策だけで十分な気がしてきました。 恐らく画像認識処理は SI CAPTCHA の文字数と画像サイズに最適化されていたのでしょう。

これでキューに入る前に弾かれるようになりました。

変更箇所

修正の仕方が広まってスパマー側に対策されるのが嫌なので、私がどのように変えたかはあえて記しません。 興味があれば、実際のコメント欄をご覧ください。(丸わかりですね)

ここでは変更箇所だけお伝えしておきます。 それだけじゃコードを修正できないという方は、是非 SI CAPTCHA プラグインのサポートページで文字数や画像サイズが可変となるよう要望を出してみてください。

  • si-captcha.php の関数 si_captcha_captcha_html() 内の以下部分の width を変更

      // url for no session captcha image
      $securimage_show_url = $si_captcha_url .'/securimage_show.php?';
      $securimage_size = 'width="175" height="60"';
      if($si_captcha_opt['si_captcha_captcha_small'] == 'true' || $label == 'si_image_side_login' ) {
        $securimage_show_url .= 'si_sm_captcha=1&';
        $securimage_size = 'width="132" height="45"';
      }
    

    これらは管理画面の CSS で変えられるかも知れません。

  • secureimage_show.php の $char_length と $img->image_width の値

宣伝!

話が全然違いますが、私もプラグイン作者なのです。 以下のプラグインも是非使ってみてください!