--------------------
◆ PukiWikiのスパム対策

--------------------

「わたしはビアンカ。わたしのこと おぼえてる?」
「そうよね。あなた まだ小さかったもんね。わたしは8才だから、あなたより2つもおねえさんなのよ」
(挨拶

2ヶ月という長すぎた沈黙を討ち破る一言は、名作「ドラゴンクエストV」より、主人公の幼馴染み・ビアンカの台詞。
主人公に意見を聞いているのに、その回答の如何に関わらず強制的に話を進めてしまうところ(他にもあるが)など、幼年期のビアンカの特徴が実によく表現されたフレーズである。
個人的には、春風のフルートを取り戻すシナリオくらいまで2人一緒に行動していても良かったとは思うが、これ以上感情移入させると物語中盤の「決断」がビアンカ寄りになってしまうかもしれないという配慮だろうか?


自分の所属するよろずサークルODプロジェクトでは、Wikiクローンの1つであるPukiWikiというソフトを使って、「ODプロジェクトWiki」というページを運営している。
ウェブではよくあることだが、以前より悪質なSPAMに悩まされ続けてきた。
本エントリでは、それらSPAMと対策について自分が分かる範囲で書いておく。





まず、ODプロジェクトWikiについて概要を説明しておく。
なお、Wiki(WikiWikiWeb)というシステム自体については、インターネット上に様々な資料があるのでそちらを参考にして欲しい。
最近はレンタルスペースとしてWikiを提供しているところも増えたので、分からない人はこの機会に軽く学んでおくと良いかもしれない。

ODプロジェクトとは、様々な企画を立ち上げ、実現に向けて研究・活動するよろずサークルである。
TRPGサークル「天翔けるゴブリン亭」や「白い夜明け亭」も、これら企画のうちの1つ。
ODプロジェクトWikiは、主に各プロジェクトの公式ページとして利用されている。
先に挙げたTRPG関連企画の他、拙作「OD TOOL」も、ここにサポートページを持つ。



誰もが自由にページを追加・編集できるというWikiの性質上、SPAMには極めて弱い。
PukiWiki公式サイトでも、様々なSPAMの手段が報告され、それに対する防衛策が議論されているようだ。
ここにある数多の資料を参考に、自分なりにPukiWikiのソースを弄ってみることにした。
なお、自分にPHPの知識は一切無い。

▼コメント欄への英文SPAMの場合


最初にSPAMが目立つようになったのは、コメント欄への英文スパム。
特に、色々なところからリンクされている「OD TOOL」のページが集中して被害に遭う。

文字コード判別 その1


まずは簡単なところから対策を施して、様子を見ることにする。
というわけで、commentプラグインを改造。
PHPについてはサッパリだが、他の箇所を参照しながら「多分こんなものだろう」と適当にコーディング。

if (mb_detect_encoding($vars['msg'])=="ASCII")
{
//スパム検出
}


コメント欄に入力されたデータの文字列エンコーディングの種別を取得してみた。
英文スパムならこれで効果はあると思われる。
他にも、文字数を数えておき、バイト数と比較するという方法もあるだろう。

文字コード判別 その2


が、この方法では当然ながらASCIIで送信されるスパムしか弾けない。
当初は効果があったのだが、次第に別の文字コードで送りつけられるスパムが目立つようになる。
ODプロジェクトWikiは基本的にeuc_jpで記述されているので、それ以外の文字コードを弾けば確実性は増すのだろうが、今度は文字コード判別ルーチンの精度が問題となってくる。
下手に実装すると、善意の投稿までもフィルタリングされてしまいかねない。

隠しパラメータの追加


掲示板荒らしなどと違って、基本的にSPAMはボットによる投稿である。
すなわち、専用のツールを使って自動的に送信してしまえというのだ。
ボットの動きを探るため、今度は簡単なロボット避けを導入してみる。

ウェブページ側
<input type="hidden" name="no_spam" value="1" />
プログラム側
if (!isset($vars['no_spam']) || $vars['no_spam'] == '')
{
//スパム検出
}


HTMLはともかく、やはりPHPは前後のソースと見比べながらフィーリングで記述。
コメント投稿フォームに、hidden属性でno_spamというパラメータを追加してみた。
PukiWikiは数多く使われているから、スパムツールの設定で自動的にPukiWikiに対応しているような場合、少しカスタマイズしただけで投稿不能になる可能性が高い。

ただ、PukiWiki専用ではないボット、つまりフォームから必要な情報を抜き出して投稿するような代物の場合は対処できない。
とは言っても、毎回必ず投稿フォームを経由するボットは少ないだろうと見て、スパム業者のデータベースが更新されるまでは一定以上の効果が期待できるのではないかと思う。
ただ、やはりというか何と言うか、「いまさらきんぐ」さん曰く、定期的に情報を更新しているらしい……とのことである。

それならそれで次の対策を立てるだけだ。
ユーザにパスワードを求めるなど、少し利便性が落ちてしまうことになるが……それは仕方ない、か。

▼新規ページ追加、およびページ編集SPAMの場合


多くのWiki同様、第3者が自由に新しいページを追加したり、既存のページを編集することが出来る。
もちろん、スパマーはこれも利用する。
てっとり早いのは、ページの新規作成・編集にパスワードをかけてしまうことだが、これだと善意のユーザが編集することは出来ない。
かと言って管理者パスワードを公開するのは愚の骨頂である。

更新履歴の強制反映


今のところ、このタイプのスパムは少ないので、まずはジャブを打って様子見としよう。
PukiWikiではページを更新した際に更新履歴に載せるかどうかを選択できるが、これを全て載せるように変更した。
こうすることでSPAMの被害にあったページを発見するのが容易くなる。

その他の方法


自分はPHPやサーバ設定に詳しくないのでこのような方法を取ったが、世間では実に色々なアプローチがなされている。
RosaGigantea.orgさんでは、POSTメソッドにだけBASIC認証をかける方法を試されたらしい。
また、Wikiではないが、掲示板改造支援サイトさんの情報もかなり役立つと思われる。

▼ファイル添付SPAMの場合


ここ最近で最も被害が酷かったのが、ファイル添付機能を悪用したスパムである。
パッと見ただけではあまり影響が無いように見えるが、被害は他のどれよりも深刻だったりする。
ブラウザの脆弱性をついて、悪意あるソフトウェアを実行させたり、他のコンピュータを攻撃してしまうウェブサイト……というのを一度くらいは耳にしたことがあると思うが、まさしくそれの土台となりうるのである。
あくまでPukiWikiのコンテンツだった上記2つのスパムと異なり、添付されたHTMLファイル等にはシステムの持つサニタイジング(無害化)機能などが働かないからだ。

ファイルの添付禁止


最も簡単なのは、ファイルの添付機能そのものを禁止してしまうことだろう。
ただし、これだと善意のユーザさえも妨げてしまうことになる。これではWikiの魅力が半減だ。
一応、管理者権限があれば添付は出来るので、ユーザにはファイルを管理者に送付してもらい、手動でアップするという回避方法も無くは無い。
これは非常に面倒だし、ファイルを渡す手間があるなら素直に外部アップローダやウェブサイトを使えば良いのである。

添付用パスワード


ユーザの手間が少し増えてしまうが、ファイルを添付する際に、添付専用のパスワードを入力してもらうことにした。
それだけだと不便なので、管理者パスワードでも問題なく通るようにしてみた。

else if(PLUGIN_ATTACH_USE_EDIT_PASSWORD && $pass !== TRUE &&
($pass === NULL || (!pkwk_login($pass) && $pass !== PLUGIN_ATTACH_EDIT_PASSWORD)) )
{
//パスワードが違う
}


あらかじめ、PLUGIN_ATTACH_USE_EDIT_PASSWORDには専用パスワードを使うかどうかのフラグが、PLUGIN_ATTACH_EDIT_PASSWORDにはそのパスワードが格納されているものとする。
パスワードそのものは書かれていないが、一般的な日本人なら必ず分かる程度のヒントを明記してあるので、おそらく通常の使用に関しては問題ないと考える。

もちろん悪意の無い外国人も多数いるし、日本人のスパム業者だって存在する。
この方法も、なかなか完璧とは言いがたい。


(2008.02.10追記)

▼ソースコード


コメントに質問が来ていたので、該当箇所のソースを実際に書いてみる。
ODプロジェクトWikiで利用しているPukiWikiのバージョンは1.4.5_1。
ただし自分はPHPを勉強したことが一切無いので、あまり信用はしないで欲しい。

とりあえずファイル添付時のパスワードのみ。コメントの方も同じような手順で制限できるはず。
添付ファイル関連のソースは、plugin/attach.inc.php に書かれている。

まず、最初のdefineが並んでいるあたりに、必要な定数をおもむろに設定。
// ファイル添付にパスワードを必要とするか(ADMIN_ONLYが優先)
define('PLUGIN_ATTACH_USE_EDIT_PASSWORD', TRUE); // FALSE or TRUE
//ファイル添付用パスワード
define('PLUGIN_ATTACH_EDIT_PASSWORD', 'pass'); //ここにパスワードを書く
//ファイル添付用パスワード
define('PLUGIN_ATTACH_EDIT_PASSWORD_HINT', 'パスワードのヒント'); //パスワード入力欄の横に表示する文章


次に、実際にファイルがアップロードされたときに、パスワードをチェックする場所を弄ってみる。
具体的には function attach_upload($file, $page, $pass = NULL) の中、else ifが並んでいるところの最後に条件を1つ加える。
//添付専用パスワード
else if (PLUGIN_ATTACH_USE_EDIT_PASSWORD && $pass !== TRUE &&
($pass === NULL || (!pkwk_login($pass) && $pass !== PLUGIN_ATTACH_EDIT_PASSWORD)))
{
return array(
'result'=>FALSE,
'msg'=>$_attach_messages['err_noparm']);
}


最後は出力されるHTML。
function attach_form($page) の真ん中辺りに、パスワード欄を出力する箇所があるので、これを修正。
上記2つとは異なり、元々あるソースを少し変更するという感じ。
$pass = '';
if (PLUGIN_ATTACH_PASSWORD_REQUIRE || PLUGIN_ATTACH_UPLOAD_ADMIN_ONLY || PLUGIN_ATTACH_USE_EDIT_PASSWORD) {
$title = $_attach_messages[PLUGIN_ATTACH_UPLOAD_ADMIN_ONLY ? 'msg_adminpass' : 'msg_password'];
$pass = '<br />' . $title . ': <input type="password" name="pass" size="8" /> ('.PLUGIN_ATTACH_EDIT_PASSWORD_HINT.')<br />';
}

[PR]
R.F.D. | by odprfd | 2007-05-15 00:22 | 情報技術

--------------------

<< Renegade soldie... | ナイト・ウィザード 第2版!の噂話 >>