スパムボットのようなコンピュータプログラムと人間を区別するために、CAPTCHA と呼ばれる仕組みが使用されています。Google や Yahoo! など大手サイトでもこの仕組みを利用して、プログラムからの攻撃に対する防御力を高めています。
このエントリでは、この CAPTCHA のうち Gimpy と呼ばれる仕組みを使ったコメントスパムボットへの対策を紹介しています。
背景
CAPTCHA とは「コンピュータと人間を区別する完全に自動化された公開チューリングテスト」の接頭句で、例えば、複数枚の絵から連想される単語を答えると云ったような、人間には簡単に解ける問題でもコンピュータプログラムにはそれが難しい、という性質を利用したものです。
Google のサイトの登録ページで見られるものは、CAPTCHA のうちでも Gimpyと呼ばれる仕組みで、MovableType でもコメントスパムボット対策として、これを利用したプラグインがあります。
James Seng's blog:Solution for
comments spamsは、
コメントの投稿時にランダムな数字列の画像から認証キーを入力するもので、
小粋空間:
MT-SCode プラグインによるコメントスパム対策(その1)でその導入方法が詳細に解説されています。
ただ、この仕組みはコメントスパムボット以外のプログラムに対しても非常に効果的ではありますが、MovableType のプラグインという形態のために再利用しにくいと言えます。せっかくですから、MovableType 以外の環境や、更には普通のログインシーケンスなどにも流用できるようにしたいものです。
Tiny Gimpy
そこで、これをヒントに MovableType 以外にも様々に流用できるような Gimpy の廉価的実装を行いました。
仮にこれを Tiny Gimpy と名付け、MovableType などの環境に依存しないよう独立した CGI として動作するようにしています。
以下にこの Tiny Gimpy を MovableType に導入する手順を紹介します。
今のところ非常に簡易的なモノなので Tiny ということで。
Tiny Gimpy
GetMD5Hash('TGimpy100.zip');
ver.1.00,
'05/06/01,
GetFileSize('TGimpy100.zip');bytes,
Perl
|
最新版のダウンロードやバグレポートはCAPTCHA の trac レポジトリでも行っています。
Step 0. 設置と動作確認
Tiny Gimpy の本体である tgimpy.cgi は、チューリングテストのための画像生成 CGI と、
パスコードのチェック用ライブラリを一つのファイルで兼ね備えています。
tgimpy.cgi をブラウザから直接実行した場合、クエリ文字列と個別の設定ファイルから
一意の数字列を含む PNG 画像を生成・表示します。
動作にはサーバに以下の Perl ライブラリがインストールされている必要があります。
とりあえず、tgimpy.cgi をサーバにアスキーモードでアップロードし、
パーミッションを 755 または 777 に設定した後、ブラウザから呼び出してみてください。
9 桁の数字列がランダムに表示されればひとまず成功です。
Step 1. パスの設定
設定ファイル(後述)の保存先に合わせて、tgimpy.cgi を修正します。
tgimpy.cgi をエディタで開き、
GetCfgPath をサーバに合わせて修正してください。
設定ファイル(次のStep.2で説明)はここで指定したディレクトリパスに保存します。
Step 2. 設定ファイルの作成
アーカイブに添付の sample.cfg を好きな名前に変え(ここでは yourblog.cfg と仮定します)、
Step 1 で設定したディレクトリパスにアスキーモードでアップロードします。
Step 3-1. MovableType の修正 1
個別エントリのテンプレートを修正し、tgimpy.cgi への呼び出しと、
コメント投稿フォームに認証用のフィールドを追加します。
<!-- 設定ファイル名の .cfg 以前の部分 -->
<input type="hidden" name="cfg" value="yourblog">
認証キー
<!-- エントリ ID をキーに認証用画像を表示 -->
<!-- cfg=yourblog の部分は上と同じ設定ファイル名に合わせてください -->
<img src="/cgi-bin/tgimpy/tgimpy.cgi?cfg=yourblog&key=<$MTEntryID$>">
を入力してください:
<input type="text" name="ans">
文字列"yourblog"を任意に変更した場合は、全ての箇所を同じにしてください。
Step 3-2. MovableType の修正 2
(MT のインストールパス)/lib/MT/App/Comment.pm を修正します。
CheckAnswer 関数が用意されているので、
これを使って入力された認証キーが正しいか否かを判定し、
不正な場合はエラーメッセージを表示します。
sub post {
my $app = shift;
my $q = $app->{query};
return do_preview($app, $q, @_) if $app->request_method() ne 'POST';
;### ここから追加
require '/home/magicvox/www/cgi-bin/tgimpy/tgimpy.cgi';
my $cfg = $q->param ('cfg');
my $ans = $q->param ('ans');
return $app->handle_error ("認証キーを入力してください")
if (! &TGimpy'CheckAnswer ($cfg, $q->param('entry_id'), $ans));
;### ここまで追加
設定ファイル(*.cfg)について
設定ファイルでは、TGimpy の動作について幾つかの項目をカスタマイズすることができます。
- szCryptKey
-
秘密キーです。
tgimpy.cgi の結果は公開キー(エントリ ID)と秘密キーから計算しています。
この秘密キーの存在が、スパムボットの作成を困難にしています。
できるだけ長く、推測されにくい文字列を与えます。
- nSizeX, nSizeY
-
tgimpy.cgi 呼び出し時に作成される認証用画像のサイズをピクセル単位で指定します。
- nFigure
-
認証用キーのケタ数を 1 から 9 の間で指定します。
トラブルシューティング - 動かない時は?
-
症状:画像が表示されない
症状:tgimpy.cgi を直接呼び出すと Internal Server Error になる
-
原因:CGI の準備に何らかの問題があります
対策:tgimpy.cgi がアスキーモードで FTP 転送されているか確認してください
対策:tgimpy.cgi のパーミッションが 755 または 777 に設定されているか確認してください
対策:tgimpy.cgi の 1 行目 #!/usr/bin/perl の部分をお使いのサーバの Perl に合わせて変更してください
対策:サーバに必要な Perl モジュールがインストールされているか確認してください → Step.0
対策:GetCfgPath を修正した個所に余計なシングルクォーテーションや全角空白がないか確認してください → Step.1
対策:設定ファイルがアスキーモードで FTP 転送されているか確認してください
対策:設定ファイルの修正した個所に余計なシングルクォーテーションや全角空白がないか確認してください → Step.2
-
症状:tgimpy.cgi?cfg=yourblog&key=1 として直接実行した時、設定ファイルの nFigure で指定したケタ数で表示されない
症状:tgimpy.cgi?cfg=yourblog&key=1 として直接実行した時、設定ファイルの nSizeX, nSizeY で指定したサイズで表示されない
-
原因:設定ファイルが正しく読み込めていません
対策:GetCfgPath が正しく設定されているか確認してください → Step.1
対策:設定ファイルが正しく配置されているか確認してください → Step.2
対策:cfg=... で指定された文字列が設定ファイルと同じか確認してください → Step.2
-
症状:コメントを投稿すると Internal Server Error になる
-
原因:MovableType への修正箇所に問題があります
対策:MovableType(Comment.pm) の修正箇所が正しいか確認してください → Step.3-2
対策:MovableType(Comment.pm) の修正箇所に余計なシングルクォーテーションや全角空白がないか確認してください → Step.3-2
対策:MovableType(Comment.pm) の require '...' の行が、正しく tgimpy.cgi を指しているか確認してください → Step.3-2
-
症状:コメントを投稿しても必ず拒否される
-
原因:必要なパラメータが CGI に正しく渡っていません
対策:コメント投稿フォームに cfg フィールドがあるか確認してください → Step.3-1
対策:コメント投稿フォームの cfg フィールドの値が設定ファイルと同じか確認してください → Step.3-1
対策:コメント投稿フォームの ans フィールドがあるか確認してください → Step.3-1
対策:img タグの tgimpy.cgi の呼び出しパラメータである cfg と key の値が正しいか確認してください → Step.3-1
対策:ans を半角数字で入力されているか確認してください
既知の問題
- コメントを直接投稿する場合の動作確認はありますが、事前にコメントのプレビューをした場合、
Tiny Gimpy に必要なパラメータを渡せません。
- TypeKey 認証との併用は未検証です。
そしてボットは居なくなった
この例では、キーとして MTEntryID を用いて tgimpy.cgi を呼び出しているため、
一つの記事に対していつでも同じパスコードが生成されています。
そのために、スパマーがこのパスコードを見た上でスパムボットを調整してやると、
そのスパムボットはその一つの記事に対してコメントすることが可能になってしまいます。
しかし、もしスパムボットを他の記事にもコメントを投稿できるようにするならば、
スパマーはこの手順を同じ数だけ繰返さなければなりません。
(この手順を自動化すること自体が CAPTCHA によって不可能に近い)
そして管理者が秘密キーを 1 つ変えてしまうだけで、その苦労は全て無駄になってしまいます。
そんなことならば、わざわざスパムボットを使ってコメントを投稿するよりも、
人間が手動で直接にコメントを投稿して回った方が早いということになるわけです。
しかし、それは既に"スパムボット対策"ではなく、単なる"人間によるコメント荒らし対策"に他ならないのです。
寄せられたコメント (全 9 件中、最新 5 件まで表示しています)
ただ,背景に例題のようなランダムドットが表示されないのです。
http://nlogn.ath.cx/archives/001627.html
原因は何だと思われますか。
GD::SecurityImage などを使わなくてもランダムドットは出せるのでしょうか。
もっとも、普通のコメントも滅多になんですけどね(笑)
次はMT4化をしたいところですが、サーバが非力なせいか、Out of memoryになっちゃうんですよね。
自宅で公開サーバを立てればいいんですけど、それはそれでセキュリティ対策に
腐心しなくてはならなくて。セキュリティ対策するためにサーバ立てるんじゃないぞと^^;;
まぁ現状で万人向けか?って言われるとそれも疑問なんですけれど、
やはりHTML+テンプレートタグ程度で編集できるのが簡単と思いますね。
> 要はエントリーIDの代わりにハッシュ値を使う点
このハッシュ値を毎回ランダムにすれば、表示される数値は毎回変わりますね。
ただ、そのハッシュが一度きりの使い捨てであることを確実にする仕組みも作らないと
結局今のエントリIDを固定で使っている場合と実質は何も変わらないんですよね…
# うーん、説明下手でスミマセン
それでも毎回同じというのは見た目的に弱そうなので(笑)改善の余地はあると思いました。
アルゴリズムの方はあまり難しくない感じがしています。例えば、時刻からランダムな文字列を作り、それをMD5でハッシュ化します。次に文字列に対応する画像を作り、ハッシュ値+拡張子という名前にしておきます。すると、CAPTCHA用のフォームはハッシュ値だけを使って作れます。
要はエントリーIDの代わりにハッシュ値を使う点以外はほとんどぴろりさんの方法で行けると思います。
フィールドや画像を埋め込む手間を省けると云う点では一理ありますね。
ただ、フィールドや画像のレイアウトを変更したいと思った時に、
テンプレート(実質は HTML)ではなくJavaScripを編集してレイアウト変更するというのは、
多くのユーザにとって敷居が高くなるかな?とも思います。
> 表示ごとにCAPTCHAコードを変えることができる
本当なら堅牢性の面からもこれは目指したいところなんですが…
ページごとに異なる隠しキーをどうやって扱うのか、という所で良い解決策が見えていません。
一応『ショボいけど導入の手間が掛からない』のバランスで言えば、今もアリかなと(笑