私のコメントスパム対策
「コメントスパム対策」と言うとき、それは複合的な意味を持ちます。コメントスパムがブログ内に表示されるのを避けたいのか、管理画面の「コメント一覧」に表示されるのすら避けたいのか、あるいはコメント投稿によって発生する再構築の負荷を低減したいのか?
このエントリーでは私がやっているコメントスパム対策について述べます。
番組の途中ですが緊急速報です。mt-comments.cgiに極めて重大な欠陥が見つかったようです。スパムメールの踏み台にされるという、とんでもない欠陥です。対策が発表されたら全員がすぐに適用する必要があります。
MT-Blacklist -> Hijacked comments.cgi
というわけで対策出ました。必ずパッチ適用をしましょう。
Movable Type Publishing Platform: Movable Type 3.15 released
Movable Type 日本語版サイト: 【重要】 Movable Typeの脆弱性と対策について
STEP 1: 「TypeKey + 承認付き」にする
単に「コメントスパムがブログ内に表示されるのを避けたい」のであれば、TypeKeyを用いた投稿だけを許すか、それに加えて「承認(事前確認)付き」でのコメント投稿を許すようにすればよいだけです。承認付きであるだけで、スパマーのスパミングに対するインセンティブは非常に小さなものになります。
STEP 2: 特定の文字列を含むコメントをdenyする
ときどき見かけるのは、ASCIIのみのコメントをdenyするパッチやプラグインです。私自身も以前同じ目的のプラグインを作りました(単純にアプリケーションレベル・コールバックの機能を試したかっただけですが)。
Ogawa::Memoranda: Application-level Callbacks in MT3.1
しかし、個人的にはASCIIのみのコメントをdenyしたくはありません。そもそも英語のブログを書いているのならdenyすべきではありませんし、この日本語ブログに英語やポルトガル語のコメントが付くこともあります。投稿者の端末の都合も考慮したいところです。最近は非ASCII文字を混ぜてくるSpambotもあるらしいので実効性にやや疑問を感じます。
私がやっているのは、コメントスパムに現れる特定の文字列に着目して、それを含むコメントをdenyするという方法です。上記のエントリーのものを若干変更して以下のようなプラグインを使っています(下記はプラグインのコア部分だけ)。
use strict;
MT->add_callback('CommentFilter', 10, 'Reject Spam Comments', sub {
my ($eh, $app, $comment) = @_;
return ($comment->text !~ /PATTERN/i);
});
1;
「PATTERN」の部分を明示していませんが、この部分には例えばコメントスパムに含まれがちな特定のタグを書いておくと非常に効果があります。以下のエントリに具体例を含むプラグインを公開しています。
Ogawa::Memoranda: Quasi-Spam Filter Plugin
STEP 3: 再構築の負荷を低減する
「TypeKey + 承認付き」にしている場合、MT 3.121までのバージョンでは承認付きコメントが投稿されたときにも再構築が発生します。したがって、STEP 2の方法でdenyできない限り、DoSとしてのコメントスパムは依然有効です。また、スパムでなかったとしても承認時点で再構築されるのが望ましいでしょう。MT 3.14では再構築が発生しませんが、「コメントが登録されたら(メールで)通知する」ように設定していても、承認付きコメントに対して通知されなくなります。
まとめると承認付きコメント投稿時の動作は、バージョンによって以下のように異なります。
| バージョン | 再構築の有無 | 通知の有無 |
|---|---|---|
| 3.121まで | あり | あり |
| 3.14 | なし | なし |
| 望ましい動作? | なし | あり |
3.14の動作でもよいのかもしれませんが、私自身は再構築「なし」、通知「あり」というのが好みなので、以下のようなパッチを当てています。このパッチはMT 3.121用です。
--- lib/MT/App/Comments.pm.bak 2004-11-24 18:43:21.000000000 +0900
+++ lib/MT/App/Comments.pm 2005-01-22 17:12:13.000000000 +0900
@@ -346,29 +346,25 @@
$blog->touch;
$blog->save;
- # Rebuild the entry synchronously so that if the user gets
- # redirected to the indiv. page it will be up-to-date.
- $app->rebuild_entry( Entry => $entry )
- or return $app->error($app->translate(
- "Rebuild failed: [_1]", $app->errstr));
- # Index rebuilds and notifications are done in the background.
- MT::Util::start_background_task(sub {
- $app->rebuild_indexes( Blog => $blog )
+ if ($comment->visible) {
+ # Rebuild the entry synchronously so that if the user gets
+ # redirected to the indiv. page it will be up-to-date.
+ $app->rebuild_entry( Entry => $entry )
or return $app->error($app->translate(
"Rebuild failed: [_1]", $app->errstr));
- my $send_notfn_email = 0;
- if (!$commenter) {
- $send_notfn_email = !$comment->visible();
- } else {
- $send_notfn_email = !$commenter_has_comment
- && !$comment->visible();
- }
- if ($blog->email_new_comments || $send_notfn_email)
- {
+ # Index rebuilds and notifications are done in the background.
+ MT::Util::start_background_task(sub {
+ $app->rebuild_indexes( Blog => $blog )
+ or return $app->error($app->translate(
+ "Rebuild failed: [_1]", $app->errstr));
+ });
+ }
+ if ($blog->email_new_comments) {
+ MT::Util::start_background_task(sub {
$app->_send_comment_notification($comment, $comment_link,
$entry, $blog);
- }
- });
+ });
+ }
}
}
MT::Util::start_background_task(
Comments and Trackbacks