というわけで [2007-04-30-1],まず世の中の CAPTCHA とかではどうやっているかというと,どうやらワンタイムトークンとするのが普通らしい.
今回のケースに置き換えると,サーバは計算問題を生成して (これがトークン),それをサーバ側に保存しておき,ユーザから送られて来た答えと,その保存しておいた問題 (の正解) と照合する.
ということでそれを実装すればよいのだけど,どうもぐっと来ない.なぜぐっと来ないのか考えた結果,思い当たったのは以下の 2 点:
ぐっと来ないものを作ってもろくなことにならないのでとっとと却下する.
というわけで考え方を変えてみる.要は,投稿のリクエストに関して,
という条件を満たせばよいのかな.これでどうだろう.
実装はとりあえず以下のような感じ.$page_template_default には
<div class="arith"> <br> コメントスパム回避のため,以下の足し算の答えを半角でご記入下さい: <br> $arith_x + $arith_y = <input class="field" name="arith" value=""> <input type="hidden" name="arith_token" value="$arith_token"> <input type="hidden" name="arith_hash" value="$arith_hash"> </div>
を仕込み,これを表示する直前に
my $arith_x = int(rand(9)) + 1; my $arith_y = int(rand(9)) + 1; my $accepted_tokens = {}; if (-e $arith_token_file) { $accepted_tokens = Storable::lock_retrieve($arith_token_file); } my $arith_token; do { $arith_token = Digest::MD5::md5_hex($logid . $q->remote_host() . int(rand(2 ** 32))); } while (defined($accepted_tokens->{$arith_token})); my $arith_hash = Digest::MD5::md5_hex($arith_token . $arith_passph . ($arith_x + $arith_y));
とする.ここでは token ファイルは読み込んでいるだけなのに注意. if ($mode eq "write") { のときの処理は,
my $arith = $q->param('arith') + 0; my $arith_token = $q->param('arith_token'); my $arith_hash = $q->param('arith_hash'); my $accepted_tokens = {}; if (-e $arith_token_file) { $accepted_tokens = Storable::lock_retrieve($arith_token_file); } if (defined($accepted_tokens->{$arith_token})) { exit; } elsif ($arith_hash ne Digest::MD5::md5_hex($arith_token . $arith_passph . $arith)) { exit; } else { $accepted_tokens->{$arith_token} = 1; Storable::lock_store($accepted_tokens, $arith_token_file); }
とした.
トークンの生成は適当で構わないのだけど,発行済みのトークンで過去に受理されたもの,および受理される可能性のあるものと重複しないようにだけは気をつける必要があるので,何かむにゃむにゃな処理をやっている.
うーむ,ちっともお手軽じゃなくなってきた.いろいろ工夫してみたところで所詮足し算を実行されたら終わりですからね.なんかベクトルが間違っている気がしないでもない.
現状の問題点:
というか何か根本的に見過ごしていることがあるような気がしてならない….どんなもんでしょ.
(追記)
2007年05月06日 kazuhooku しょうもないつっこみしてすみません。よっぱらいつつの感想: syncookies のようなゼロ記憶だと難しいのかな。
http://b.hatena.ne.jp/entry/http://www.kagami.org/diary/2007-05-05-1.html
恥ずかしながら syncookies って初耳だったのでちょっと勉強.
なるほど.トークンを乱数を使って作る代わりに時刻を使うことにして,一定時間経過してたら reject するってことになりますかね.ハッシュ値の計算に時刻をつっこんでおくことで,サーバ側の保存無しで expire をチェックできるところがミソだと理解しました.
「一定時間」をうまく設定してあげる必要があるかも.短いとコメントを書いているうちに expire しちゃうし,長いとその間は固定パラメータ攻撃されちゃいますよね.まあ 1 時間くらいにしておけば実用上十分な気がしますが.
…というのが SYN cookies の動作を単純にマッピングした場合の話だと思いますけど,もしかするともっとうまい応用のしかたがあったり…?
最終更新時間: 2009-01-04 15:31