return

掲示板に対する悪戯の防止策


 ここでは、掲示板に対する悪戯を、悪戯防止ライブラリ zddbbs.pl によって、どのように防ぐかを解説します。
 私(zddbbs.pl の作者)自身は、インターネットを始めて1年ばかりで、unixやネットワークについても十分な知識がある訳ではないので、内容に間違いや誤解があった場合はぜひご指摘下さい。

掲示板 , E-mail


 また、掲示板への悪戯の一般的な解説は、「ゲスッ・メモリアル」などに詳細に説明されていますので、そちらを参照してください(ここよりは、よほど読みやすくて分かりやすいと思います(^^;)。


 以下では、広く普及している掲示板として Web裏技 の minibbs.cgi を例に挙げていますが、この掲示板スクリプトが特に悪戯に無防備であるとか悪戯され易いという訳ではありません。外部投稿の禁止機能(ただし、規定値は外部投稿可能)は Ver.7 以前から設けられていたし、Ver.8 あたりから GET METHOD による書き込みを禁止したり、メッセージの文字数を制限することもできるようになっているので、むしろ、他の無対策の掲示板スクリプトに比べれば安全性は高いと思われます。ただ、普及率が高くスクリプトの内容が多くの人に読まれているため、ターゲットになりやすいという事情はあるかもしれません。

投稿者の特定

 掲示板に悪意のある投稿が行われた場合は、まずこの投稿者を特定することが重要になると思われます。
 ただ、インターネット上では、投稿者の具体的な氏名などを特定することは通常はできません。しかし、具体的ではなくても、利用しているISP(プロバイダ)や所属するサイトなどが分かるだけでも有用です。また、これらが特定できないという情報も、普通のHP(ホームページ)ではアクセス制限を行う上では役に立つ情報となり得ます。

 なお、クッキーを使えば投稿者の特定は可能になりますが、これはあくまでクッキー対応のブラウザを用いる善意の投稿者にのみ有効であって、クッキー自体は受け入れを拒否することもできるし内容を改変や削除することも可能なので、これに頼ることは難しいでしょう。

IPアドレス

 投稿者のIPアドレス(minibbs.cgiのリモートホスト欄に表示されるドメインネームも同じ)が分かれば(それが盗用されたものなどでないかぎり)、そのサイトで記録されたログから投稿者の氏名などの特定は可能です。ただし、ISPなどの場合、通信の秘守義務やプライバシーの保護などのため(刑事事件にでもならないかぎり)、これを公表することはないと思われます。しかし、誠意のあるISPであれば、投稿内容やそのIPアドレスとアクセス時間のリストを示すことにより、悪意があると判断されれば、当人に対して何らかの適当な措置を講じてもらえることが期待できるのではないかと考えています。実際に、どの程度のことをしてもらえるのかは分かりませんが、たとえばここBiglobeでも、いたずらや嫌がらせに対する窓口があるようです。
 誠意のないISPやその他の非協力的なサイトなどの場合には、このような措置が期待できないので、この場合は、そのIPアドレスによるアクセスを制限するほかありません。IPアドレスによるアクセス制限は、.htaccess などによる方法がありますが、この方法では、同じIPアドレスを使う全ての人のアクセスを禁止することになり、しかも、掲示板への書き込みだけでなく閲覧さえも禁止されることになるので、一般のISPのIPアドレスなどのように複数の人が共用するIPアドレスを設定する場合には慎重に取り扱う必要があります。
 zddbbs.pl では、このようなIPアドレスをCGIレベルで排除することができます。これですべてが解決する訳ではありませんが、たとえばこのIPアドレスを登録することにより、書き込みは禁止しても閲覧は許すことができるので、まったく関係ない人に門前払いを食わせるようなことは避けることができます(check_user 関数の引数*IPADDR参照)。また、このような書き込みを拒否する際に、適当なメッセージを表示することもできます。
 もっとも、投稿者の目的がサーバーへの負担を増大させることにあるような場合には、CGIレベルでの制限は意味がなくなります。しかし、このような掲示板への攻撃は、単にそのHPだけの問題ではなく、サーバー全体の問題であるため、たとえばHPを設置しているISPなども含めて別の方法による対策を講じる必要があると思われます。

プロキシサーバー

 プロキシ(プロクシ)サーバーを経由したアクセスの場合、CGIの環境変数REMOTE_ADDRで示されるIPアドレスは、最後に経由したプロキシサーバーのものとなり、投稿者の本来のIPアドレスが隠蔽されます(HPのサーバーにとっては、プロキシサーバーとお話しているにすぎないので、このこと自体は普通のことです)。しかし、通常は、環境変数HTTP_VIAなどが設定されているかどうかを検査することによりプロキシサーバー経由かどうかを調べることができます(check_user 関数の引数PROXY1参照)。ただし、会社や学校からのアクセスの場合には、必ずプロキシサーバーを経由することが多いので、ここでプロキシサーバー経由と判断したものを全て排除することは問題があります(プロキシサーバー経由の投稿者が全て怪しい訳ではない)。
 プロキシサーバーを経由したアクセスの場合には、環境変数HTTP_X_FORWARDED_FORに経由したサイトのIPアドレスが列挙される場合があります。そこで、この環境変数の先頭のIPアドレスを調べれば、投稿者の本来のIPアドレスを知ることができます。zddbbs.pl では、このようにして調べたIPアドレスを環境変数REMOTE_ADDRで示されるIPアドレスと共にドメインネームに変換して取得することができます($DomainName 参照)。
 しかし、プロキシサーバーによっては、環境変数HTTP_X_FORWARDED_FORが設定されても、IPアドレスを正確に申告しないものがあります。たとえば、先頭のIPアドレスを設定しなかったり unknown にするものがあります。zddbbs.pl では、このような奇妙なプロキシサーバー経由の投稿者を検出して掲示板への書き込みなどを禁止することができます(check_user 関数の引数PROXY2参照)。
 環境変数HTTP_X_FORWARDED_FORが設定されないプロキシサーバー経由の場合には、環境変数HTTP_VIAを検査する方法もあります(check_user 関数の引数*VIA参照)。また、プロキシサーバーによっては、これらの環境変数を設定させず、プロキシサーバー経由であることの痕跡を消し去るものもあります。これらの場合は、zddbbs.pl が記録するログ(rec_logfile 関数の引数LOGFILE(zddbbs.log)参照)を調べて、そのプロキシサーバーのドメインネーム(引数*VIA)やIPアドレス(引数*IPADDR)を登録することによって排除します。プロキシサーバーによっては、HTTP_USER_AGENTを書き換える場合があるので、これを検査してもいいかも知れません(check_user 関数の引数*AGENT参照)。
 なお、複数のプロキシサーバーを経由してアクセスを行う場合、最後のプロキシサーバーはマットーなものである可能性があります。このような場合、環境変数HTTP_X_FORWARDED_FORが設定されているなら、環境変数REMOTE_ADDRのIPアドレスではなく、このHTTP_X_FORWARDED_FORの最初の方に記録されたIPアドレスを設定する必要があります。

プロキシサーバー経由の書き込み制限の具体例
●もっとも厳しい制限
 悪戯の防止を最優先する場合の設定例です。

 push( @ipaddr, '123.45.67.8' ); # 例示のIPアドレスはデタラメです
 push( @ipaddr, '124.56.7.89' ); # 例示のIPアドレスはデタラメです
 push( @agent, 'via' );
 push( @agent, 'proxy' );
 push( @permission, '123.45.67.8' ); # 例示のIPアドレスはデタラメです
 if ( &zddbbs'check_user( 1, 1, 0, 0, *ipaddr, '', *agent, *permission ) ) { &error; }

 check_user関数の第2引数を 1 にすることにより、プロキシサーバー経由の場合に設定される環境変数が存在する場合に全てエラーにします。これでプロキシサーバー経由の書き込みの大部分を禁止することができます。
 ただし、匿名のアクセスを目的とするプロキシサーバーの場合(プロキシサーバーでない可能性もあります)には、これらの環境変数を設定しないことがあります。もっとも、このようなプロキシサーバーも、環境変数HTTP_USER_AGENTに via などの文字を追加する場合があるので、第7引数の配列 agent にこれらの文字を登録します( push( @agent, 'via' ); など )。
 しかし、このような痕跡もまったく残さないプロキシサーバーの場合には、第6引数の配列 ipaddr に直接このIPアドレスを登録して排除する他ありません( push( @ipaddr, '123.45.67.8' ); など )。このIPアドレスは、事前に分かっている場合には予め登録します。また、運用後にこの投稿者検査をパスして来た悪戯があった場合には、IPアドレスをログから読み出して追加して登録します(この場合は、環境変数REMOTE_ADDRのIPアドレスを直接登録できます)。
 なお、このような厳しい制限を行うと、会社や学校などからのプロキシサーバー経由の正規の書き込みまで禁止されることになります。この場合は、書き込み禁止時のエラーメッセージなどで投稿者にメールでの連絡を促し、このようなプロキシサーバーのIPアドレスを第8引数の配列 permission に登録して書き込みを個別に許可するようにしなければなりません。

●一般的と思われる制限

 push( @ipaddr, '123.45.67.8' ); # 例示のIPアドレスはデタラメです
 push( @ipaddr, '124.56.7.89' ); # 例示のIPアドレスはデタラメです
 push( @agent, 'via' );
 push( @agent, 'proxy' );
 push( @permission, '123.45.67.8' ); # 例示のIPアドレスはデタラメです
 if ( &zddbbs'check_user( 1, 0, 1, 0, *ipaddr, '', *agent, *permission ) ) { &error; }

 check_user関数の第3引数を 1 にすることにより、プロキシサーバー経由の場合に設定される環境変数HTTP_X_FORWARDED_FORを検査します。環境変数HTTP_X_FORWARDED_FORには、元のIPアドレスと経由したプロキシサーバーのIPアドレスが列挙されるので、これらの最初にちゃんとしたIPアドレスが記録されていない場合にエラーにします。これで環境変数HTTP_X_FORWARDED_FORにIPアドレスをちゃんと記録するプロキシサーバー経由の書き込みが可能となるので、会社や学校などからの正規のアクセスを可能にします。
 ただし、この場合は、環境変数HTTP_X_FORWARDED_FORを設定しないプロキシサーバー経由の書き込みはすべて可能になります(正規の投稿者であっても、このようなプロキシサーバー経由でなければアクセスできない場合があります)。もっとも、このようなプロキシサーバーは、通常環境変数HTTP_USER_AGENTに via などの文字を追加することがあるので、第7引数の配列 agent にこれらの文字を登録します( push( @agent, 'via' ); など )。
 そして、以降は、もっとも厳しい制限の場合と同様に、それぞれのケースに応じて個別に書き込みを禁止したり許可することにより正規の投稿者とそれ以外の振り分けを行います。なお、第6引数の配列 ipaddr にIPアドレスを登録する場合、環境変数HTTP_X_FORWARDED_FORが設定されているならば、環境変数REMOTE_ADDRのIPアドレスではなく、このHTTP_X_FORWARDED_FORの最初の方に記録されているIPアドレスを登録します。


投稿手段


GET METHODによる書き込みや外部投稿

 「イメージ見せかけタグ」によるGET METHODでの外部投稿の悪戯はよく知られているので特に説明しませんが(上記「ゲスッ・メモリアル」に詳しい説明があります)、minibbs.cgi Ver.8.8 であれば、$method=1;のままにしておけばGET METHODでの書き込みを禁止し、$ref_axs=1;を設定すれば外部投稿を禁止することができます。また、Ver.7.5 あたりでも「HTTP_REFERER」の文字を探して注記に従ってコメントを外せば外部投稿を禁止することができます。ただし、ブラウザ(サーバーも?)によっては、外部投稿を禁止すると、正規の投稿もできなくなる場合があるという点に注意してください。
 掲示板スクリプトにこれらの機能がない場合には、zddbbs.pl でも検査できるようになっています(check_post 関数参照)。
 外部投稿の禁止は、JavaScriptなどを利用した安易な自動書き込みも防止することができます。

自動書き込み

 外部投稿を禁止する際に使用する環境変数HTTP_REFERERは、ブラウザが自主的に申告したものをサーバーがCGIの環境変数に設定するものなので、たとえばperlやJavaを使って自作のWebクライアントを作成すれば、この環境変数HTTP_REFERERにウソのURLを設定することも可能になります(ブラウザにそのようなウソのURLを申告させるプラグインのようなものもあるそうです)。従って、このようなWebクライアントを用いれば、上記外部投稿の禁止を回避することができるようになります。
 この種のWebクライアントとしては、perlのCGIを用いたPerlDUKEが有名です。私もこれを見たことが、悪戯防止ライブラリの作成動機となりました(実際にはこのCGIを呼び出すFORMを見ただけですが)。本家の方は、私が以前見たURLからは撤去されたようなので、悪戯防止ライブラリを作成する際の参考に作ったPerlDUCKを展示しておきます。「Referer」の欄に入力されたURLがHTTP_REFERERに設定されるので、これを「Target」の欄と同じにしておけば上記外部投稿の禁止を回避できます。また、POST METHODでリクエストを発するので、GET METHODによる書き込み禁止も意味がありません。
 なお、本家の方は、高速で大量のメッセージを連続して投稿できるようになっているようですが(このへんにノウハウがあるのかも知れませんが)、展示したPerlDUCKの方は、この点大幅に骨抜きになっています。つまり、投稿のたびにお行儀よく成功のレスポンスを待って次の投稿を行い、エラーが返ると以降の投稿を中止するので、単なる自動投稿機にすぎないものになっています。ただ、このような掲示板攻撃ツールは、話だけではどのようなものなのかなかなか理解しにくいと思うので、展示したもどきでも雰囲気は伝わるのではないでしょうか。ターゲットとなる掲示板も用意してあるので、試してみてください。オリジナルの掲示板は、外部投稿を禁止するようにしていますが、PerlDUCKを使えば書き込めてしまいます。また、悪戯対策を施した掲示板も、怪しいプロキシサーバーを経由したりしなければ、最初の1回は書き込めてしまいます(もっとも、これでPerlDUCKと悪戯防止ライブラリとの対決の決着が付いた訳ではありませんが…)。
 ただし、このようなPerlDUCKであっても悪用することは可能であり、そのようなことになれば本末転倒となるため、登録したURL以外には投稿できないなどの種々の制限を設けてあります。

FORMのNAME属性の名前

 外部書き込みを行う場合には、FORMのNAME属性の名前を本来のものと同じにする必要があります。そこで、minibbs.cgi のようによく知られた掲示板スクリプトを使う場合には、このNAME属性の名前をオリジナルとは違うものに変えておくことにより、安易な悪戯を防止することができる場合があります。もちろん、投稿者側でも、この変更に合わせればそれまでですが、オリジナルのままでは投稿者側に同じFORMを使えるという利便を与えることになります。たとえばここに展示したPerlDUCKのFORMでは、Targetを変更するだけで、オリジナルのminibbs.cgiであれば、どこのURLに設置したものでも書き込み可能になります(TargetとなるURLの制限がなければの話ですが)。
 また、このNAME属性の名前を自動的に変化させるようにすれば、より確実かも知れません。たとえば、1時間単位の現在時刻をcryptして名前に使用すれば、読み込み側でも同じ手順でこの名前を得られます(現在時刻だけでなく1時間前の時刻も使って試す必要がある)。これで名前の有功期限を1〜2時間に限定することができます。

書き込み内容の検査


書き込み文字数

 掲示板スクリプトは、通常はサーバーの容量と負担を考慮して、所定メッセージ数以上の書き込みがあった場合には古いメッセージから順に削除されるようになっています。しかし、1メッセージの文字数が制限されていない場合には、一度に大量のメッセージを書き込まれるおそれがあるので、メッセージ数だけを制限したのでは問題があります。
 zddbbs.pl では、この投稿メッセージの文字数を検査できるようになっています(check_size 関数参照)。この関数では、メッセージ本文だけでなく名前やタイトルなどの全ての入力欄を合計したバイト数を検査します。
 なお、minibbs.cgi の Ver.8.8 でも $max_size にバイト数を設定することにより、書き込み文字数を制限できるようになりました。しかし、実際には、メッセージ本文のバイト数のみを検査しているので、他の入力欄(タイトル欄など)のバイト数が多くても書き込みを禁止することができません。もちろん、minibbs.cgi が出力したFORMからの投稿であれば、これらの入力欄に書き込む文字数がブラウザによって制限されるので問題は生じませんが、外部のFORMから上記の自動書き込みツールを用いて投稿すれば、このような制限は無意味になります。

連続書き込み

 上記の自動書き込みツールを用いた投稿や手動による連続書き込みなどの場合には、普通は同じメッセージが繰り返し書き込まれます。zddbbs.pl では、前回の投稿メッセージをファイル(zddbbs.dat)に保存しておくことにより、同じ内容のメッセージの重複投稿を禁止することができます(check_dup 関数参照)。この際、メッセージは完全に一致した場合だけでなく、ほとんど同じ(4分の3程度同じ)という場合も検査することができるので、メッセージをわずかに変更しただけの連続書き込みを防ぐことも可能です。ただし、この場合には、ほとんどが前のメッセージの引用であるような書き込みも排除するおそれがあるので、注意が必要です。
 また、この関数では、同一人(環境変数REMOTE_ADDRが同じ投稿者)が指定時間内に連続して書き込みを行うことも禁止できます。この機能だけを利用した場合でも、送信ボタンの連打のような悪戯は防止することができます。
 なお、このように連続書き込みを禁止すると、誤って一般の投稿者が二重投稿してしまうミスも防止できます。

不適切な言葉を含む書き込み

 掲示板のメッセージで、絶対に書き込んで欲しくない言葉があれば、あらかじめ登録しておくことにより、このような言葉を含むメッセージの投稿を禁止することができます(check_str 関数参照)。
 ただし、たとえば「死ね!」という言葉を登録した場合、「男の子って、そういう時は必死ね!」というような問題のないメッセージまで、偶然の一致によって書き込みが禁止されてしまうので、登録する言葉は慎重に選択する必要があります。
 なお、この関数は、登録した言葉が指定回数以上あった場合にのみ書き込み禁止とすることも可能です。

内容のない書き込み

 たとえば、「XXX」などの1種類の文字だけからなるメッセージや「バカ!バカ!バカ!バカ!」などの単純な繰り返しだけのメッセージの書き込みも、zddbbs.pl によって投稿を禁止することができます(check_frq 関数参照)。

タグの書き込み

 掲示板へのタグの書き込みはいろいろ問題があるので、禁止するのが一番だと思います。このため、zddbbs.pl でもタグに関する検査は用意していません。

 タグを許可する場合は、<PLAINTEXT>や<XMP>、<SERVER>タグなどのような厄介なものが現れても問題が生じないように、禁止タグを設定するよりも、許可タグを設定する方がよいと思われます。(ただし、<FONT>のように、スタイルシートの導入により危険なタグに変身した場合もありますが…)
 タグの閉じ忘れなどの問題については、以前「とほほのWWW入門」「ラウンジ」に中途半端なスクリプトを書き散らかしたままになっているんで、いずれまとめてみたいと思っています。

 なお、minibbs.cgi の Ver.7.5 あたりでは、タグを禁止しても、URL欄にタグが書き込めてしまうという問題があります。たとえば「<B>TEST</B>」のようなタグをURL欄に書き込んでみて、もし太字で表示されるようであれば、

  $FORM{'subject'} =~ s/</&lt;/g; $FORM{'subject'} =~ s/>/&gt;/g;

の行(Ver.7.5 の非TABLEタイプなら328行目)の次に、

  $FORM{'hpage'} =~ s/</&lt;/g; $FORM{'hpage'} =~ s/>/&gt;/g;

を挿入することで修正できます。
 minibbs.cgi の Ver.8.8 では、この不都合は直っています。

 なお、悪戯防止ライブラリを使用すれば、掲示板などへの書き込みだけでなく閲覧自体も禁止することが可能です。また、単に気に入らないという理由だけで特定の投稿者を排除したり、嫌いなブラウザを締め出すようなことも可能です。
 しかし、この悪戯防止ライブラリは、インターネットを利用したコミュニケーションの健全な発展を期待して作成したものなので、必要以上のアクセス制限を行うためには使用されないことを望みます。

written by live@a.biglobe.ne.jp