IT実践

Google reCAPTCHA v3をコピペで簡単に導入

PukiWikiのコメントスパム対策にGoogle reCAPTCHA v3を導入してみようと思ったのですが、検索してもWordPress前提の記事ばかりでしたので、具体的なソースコードを公開しておきます。

プラグインをわざわざ導入しないでもPHPで書かれたシステムであれば流用できます。

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要素に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>

【3】フォーム送信後の処理を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行目に注意してください。
処理の開始前(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に変更・再起動して解決です。