SPAM対策メモ
※以前別の場所で書いた文章を備忘的に書き記しておきます。
【投稿年月日】2010-08-10 【ジャンル】PHP/MySQL | JavaScript
弊社が運営する「最速資産運用」では、一言コメントを投稿できるのですが、最近スパムを受けるようになりました。たいした被害ではないので放置するつもりでしたが、その都度メールで通知されるのが鬱陶しくなったので、やむを得ず対策をとることに。参考にしたのは以下のサイトです。
掲示板のスパム対策
www.j-fm.net/bbs.htm
この中の「スパマーの手法を逆手にとって欺く方法」において、2つの方法が例示されていました。
その1 コントロール名を偽装する
たとえばコメント入力欄を「email」、メールアドレス入力欄の名前を「comment」と言う名前にしておくと、スパムプログラムはemailデータとして、コメント内容を送り込んできますので、見ればすぐにスパムというのが見破れるってものです。
ふむふむ、これなら簡単にできるぞ、と「input」や「textarea」の「name」を書き換えた直後、なぜかスパム来襲の頻度が上がりました。少なくとも、この方法は、既にスパムを受けている場合には使えないということが分かりました。。その2 投稿時にリアルタイムにキーデータを書き換える
Hidden属性でキーを埋め込むまでは、基本の方法と同じですが、投稿ボタンをクリックしたら、データを書き換えてから投稿を実行するようにします。
具体的なイメージがつかめなかったのでサンプルのスクリプトを確認しましたが、Perlで記述されていたこともあり理解できませんでした。その間にもスパムが次々届くので、かなりイライラします。スパム対策前のソース(「post.php」「main.js」)は以下の通り。
なお、このフォームにおいては、投稿文を入力する「textarea」にカーソルをあてると表示されていた「ご意見ご要望をお聞かせください」が消え、カーソルを外すと「ご意見ご要望をお聞かせください」が再度表示されるというJavaScriptを実装しています。
post.php
<?php
function formEcho() {
echo '<form action="./form.html" method="post">
<textarea name="message" id="message" cols="35" rows="3" onfocus="setbg(this);" onblur="setbg(this, 1);">ご意見ご要望をお聞かせください</textarea>
<input name="submit" value="送信" type="submit" />
</form>';
}
function postJd() {
$body = $_POST['message'];
if($body) {
// POSTデータに問題なかったら処理を続行
}
}
?>
function formEcho() {
echo '<form action="./form.html" method="post">
<textarea name="message" id="message" cols="35" rows="3" onfocus="setbg(this);" onblur="setbg(this, 1);">ご意見ご要望をお聞かせください</textarea>
<input name="submit" value="送信" type="submit" />
</form>';
}
function postJd() {
$body = $_POST['message'];
if($body) {
// POSTデータに問題なかったら処理を続行
}
}
?>
main.js
function setbg(e, id) {
msg = e.defaultValue;
$("message").value = (!id && e.value==msg) ? '' : msg;
}
function $(tagId) {
return document.getElementById(tagId);
}
msg = e.defaultValue;
$("message").value = (!id && e.value==msg) ? '' : msg;
}
function $(tagId) {
return document.getElementById(tagId);
}
スパムはJavaScriptへの対応が不十分らしいので、上記「function setbg()」を利用して、リアルタイムにキーデータを書き換えることにしました。
- キーデータとして、リアルタイムで変動するUNIX時間を利用する。
- 投稿文を入力する「textarea」にカーソルをあてると、上記キーデータが埋め込まれる。
- POSTデータの受取時に、上記キーデータを利用することで1時間以内に投稿されたもの以外を除外する。
- 上記キーデータは「base64_encode」などを使って(少しだけ)難読化する。なお、POSTデータの受取時には「base64_decode」などを使って戻す。
post.php
<?php
function formEcho() {
echo '<form action="./form.html" method="post">
<textarea name="email" id="email" cols="35" rows="3" onfocus="setbg(this);" onblur="setbg(this, 1);">ご意見ご要望をお聞かせください</textarea>
<input name="message" value="送信" type="submit" />
<input name="body" id="message_body" value="'.base64_encode(date('U')).'" type="hidden" />
<span id="message_id_"></span>
</form>';
}
function postJd() {
$body = $_POST['email'];
$body_jd = base64_decode($_POST['mail']);
if($body && $_POST['mail'] && $body_jd+3600>date('U') && $body_jd<date('U')) {
// POSTデータに問題なかったら処理を続行
}
}
?>
function formEcho() {
echo '<form action="./form.html" method="post">
<textarea name="email" id="email" cols="35" rows="3" onfocus="setbg(this);" onblur="setbg(this, 1);">ご意見ご要望をお聞かせください</textarea>
<input name="message" value="送信" type="submit" />
<input name="body" id="message_body" value="'.base64_encode(date('U')).'" type="hidden" />
<span id="message_id_"></span>
</form>';
}
function postJd() {
$body = $_POST['email'];
$body_jd = base64_decode($_POST['mail']);
if($body && $_POST['mail'] && $body_jd+3600>date('U') && $body_jd<date('U')) {
// POSTデータに問題なかったら処理を続行
}
}
?>
main.js
function setbg(e, id) {
msg = e.defaultValue;
$("message").value = (!id && e.value==msg) ? '' : msg;
if(!$("message_id_").innerHTML) {
$("message_id_").innerHTML = '<input name="mail" id="mail" value="' + $("message_body").value + '" type="hidden" />';
}
}
function $(tagId) {
return document.getElementById(tagId);
}
msg = e.defaultValue;
$("message").value = (!id && e.value==msg) ? '' : msg;
if(!$("message_id_").innerHTML) {
$("message_id_").innerHTML = '<input name="mail" id="mail" value="' + $("message_body").value + '" type="hidden" />';
}
}
function $(tagId) {
return document.getElementById(tagId);
}
ポイントは、UNIX時間やエンコード・デコードについては、利用していることを隠すために、JavaScriptではなく、PHPにおいて記述していることです。スパムプログラムを構築するためには、当然ながらロジックが必要とされます。スパム発信者には、PHPにおいてどのような処理をしているのか、ロジックを推測することが困難なので非常に効果的だと思われます。
特に1時間以内に投稿しなければスパム判定をするというロジックは有効だと思います。なお、上記「base64_encode(date('U'))」と「base64_decode($_POST['mail'])」については、例えば「base64_encode(date('U')/5)」と「base64_decode($_POST['mail'])*5」のように、任意の数字を乗除することでさらに分かりにくいものとなります。
ユーザーがJavaScriptを利用していない場合は一言コメントが投稿できませんが、全ページに以下のように記述しているし、また、本当に何かを伝えたかったら公開されているメールアドレス宛にメールをすればいいだけなので許容範囲内だろうと考えています。
<noscript>※お使いのブラウザはJavaScriptに対応していないか、または無効になっています。当サイトでは、主要コンテンツにおいてJavaScriptを使用しているので、JavaScriptの設定を有効にすることをお勧めします。</noscript>
スパム対策後
上記対策後、スパムは全くこなくなりました。似たような仕組みを、運営している他のサイトにも適用することを考えています。
【追記】
そう言えば、EDIUNETのデータを久しぶりに更新したけど、あれってTwitterのAPI利用したコメント投稿機能があるんだよな。スパムについて確認した方がいいかも。(2010-08-10)EDIUNET | PHP/MySQL | 独り言 | 提供サービス | JavaScript