PukiWikiのコメントスパム対策にGoogle reCAPTCHA v3を導入してみようと思ったのですが、検索してもWordPress前提の記事ばかりでしたので、具体的なソースコードを公開しておきます。
プラグインをわざわざ導入しないでもPHPで書かれたシステムであれば流用できます。
WordPress上のContact Form 7に表示される「reCAPTCHA v3バッジを非表示にする方法」は下記の別記事をご覧ください。
Google reCAPTCHAにサイトを登録する
まずGoogle reCAPTCHAでドメインを追加してreCAPTCHAのキーを2種類(サイトキーとシークレットキー)取得しておきます。
送信フォームを修整する
【1】フォームを表示する部分にJavaScriptを追加
インプットフォームが設置してあるページに以下を加えます。
挿入する場所は<head>と</head>の間が一般的ですが、ページ表示速度を考えると</body>直前が良い等いろいろありますが、そのあたりは今回触れません。
私の場合は、<form>と</form>の間に挿入しました。
手書きのHTMLならまだしも、最近はほとんどのコンテンツがPHPによる出力になっていますので、ヘッダ出力部分に追記するだけですと他の全てのページにまで挿入されてしまい、表示速度に悪影響を与えてしまいます。ですのでヘッダ出力部分に「インプットフォームがある場合にのみ挿入」という振り分け処理を入れる必要があるのですが、将来的にインプットフォームの表示ページ名等に変更があった際に、ヘッダ部分の書き換えを覚えている自信もないので、<form>~</form>にまとめたという次第です。
<script src="https://www.google.com/recaptcha/api.js?render=ここにサイトキーを入れる"></script>
<script>
grecaptcha.ready(function() {
grecaptcha.execute('ここにサイトキーを入れる', {action: 'homepage'}).then(function(token) {
var recaptchaResponse = document.getElementById('recaptchaResponse');
recaptchaResponse.value = token;
});
});
</script>
【2】formにinput要素に1行追加する
上記のJavaScriptのrecaptchaResponseという名前を変更していない限り、これはこのままコピーで問題ありません。
<input type="hidden" name="recaptchaResponse" id="recaptchaResponse" />
上記【1】と【2】を合わせるとこんな感じになると思います。
<form action="$script" method="post" class="comment_form">
<script src="https://www.google.com/recaptcha/api.js?render=ここにサイトキーを入れる"></script>
<script>
grecaptcha.ready(function() {
grecaptcha.execute('ここにサイトキーを入れる', {action: 'homepage'}).then(function(token) {
var recaptchaResponse = document.getElementById('recaptchaResponse');
recaptchaResponse.value = token;
});
});
</script>
<input type="text" name="name" placeholder="お名前" />
<input type="text" name="msg" placeholder="メッセージ" />
<input type="hidden" name="recaptchaResponse" id="recaptchaResponse" />
<input type="submit" name="comment" value="submit" />
</form>
フォーム送信後の処理をPHPで記述
書く場所さえ確定してしまえば非常に簡単な作業です。
昔のcgiプログラム等を使っていない限り、通常は【2】のフォームを出力するファイル自身になります。
HTML出力とは別のファイルが指定されている場合は「<form action=”この部分”>」を見て、そちらのファイルに挿入してください。
if (isset($_POST['recaptchaResponse']) && !empty($_POST['recaptchaResponse'])) {
$secret = 'ここにシークレットキーを入れる';
$verifyResponse = file_get_contents('https://www.google.com/recaptcha/api/siteverify?secret='.$secret.'&response='.$_POST['recaptchaResponse']);
$reCAPTCHA = json_decode($verifyResponse);
if ($reCAPTCHA->success) {
// たぶん人間
} else {
// ボットかも
return;
}
}
9行目のreturnに注意してください。
処理の開始前(function内の一番最初)に条件文を入れましたので、ボット判定された場合にはその後の処理をしないでリターンするようにしてあります。
人間と判定された場合には何もせず(6行目)、挿入前と同じ処理を行うという仕組みです。
PukiWikiでの設定はcommentプラグインに挿入
PukiWiki 1.5.2のコメント欄対策としては、comment.inc.phpに書くだけです。
.
.
.
define('PLUGIN_COMMENT_FORMAT_NOW', '&new{$now};');
define('PLUGIN_COMMENT_FORMAT_STRING', "\x08MSG\x08 -- \x08NAME\x08 \x08NOW\x08");
function plugin_comment_action()
{
global $vars, $now, $_title_updated, $_no_name;
global $_msg_comment_collided, $_title_comment_collided;
global $_comment_plugin_fail_msg;
// Google reCaptcha v3 -------------------
if (isset($_POST['recaptchaResponse']) && !empty($_POST['recaptchaResponse'])) {
$secret = 'ここにシークレットキーを入れる';
$verifyResponse = file_get_contents('https://www.google.com/recaptcha/api/siteverify?secret='.$secret.'&response='.$_POST['recaptchaResponse']);
$reCAPTCHA = json_decode($verifyResponse);
if ($reCAPTCHA->success) {
// たぶん人間
} else {
// ボットかも
return;
);
}
}
// END --- Google reCaptcha v3 ----------
if (PKWK_READONLY) die_message('PKWK_READONLY prohibits editing');
if (! isset($vars['msg'])) return array('msg'=>'', 'body'=>''); // Do nothing
<br />
<form action="$script" method="post" class="comment_form">
<script src="https://www.google.com/recaptcha/api.js?render=ここにサイトキーを入れる"></script>
<script>
grecaptcha.ready(function() {
grecaptcha.execute('ここにサイトキーを入れる', {action: 'homepage'}).then(function(token) {
var recaptchaResponse = document.getElementById('recaptchaResponse');
recaptchaResponse.value = token;
});
});
</script>
<div>
<input type="hidden" name="plugin" value="comment" />
<input type="hidden" name="refer" value="$s_page" />
<input type="hidden" name="comment_no" value="$comment_no" />
<input type="hidden" name="nodate" value="$nodate" />
<input type="hidden" name="above" value="$above" />
<input type="hidden" name="digest" value="$digest" />
$nametags
<input type="text" name="msg" placeholder="メッセージ" id="_p_comment_comment_{$comment_no}" />
<input type="hidden" name="recaptchaResponse" id="recaptchaResponse">
<input type="submit" name="comment" value="$_btn_comment" />
</div>
</form>
EOD;
return $string;
}
はまったポイント
ブラウザから直接以下を試してみるとreCAPTCHAに正常にデータが送られていないというエラーメッセージが表示される。
https://www.google.com/recaptcha/api/siteverify?secret=シークレットキー&response=POSTで取得したrecaptchaResponse
2分以内でないとタイムアウトエラーとなりますが、そうでは無くデータが正常に送られていないとのエラー。
{
"success": false,
"error-codes": [
"invalid-input-response"
]
}
最終的にたどり着いたのが、phpのfile_get_contentsで、外部URLへの接続が許可されていませんでした。
php.iniの設定でallow_url_fopen=on
に変更・再起動して解決です。