reCAPTCHA v3 とは、クローラーやロボットによるアクセスを制限して、無駄にサイトアクセスさせないために設置する認証装置です。
主に、連投されては困るようなコンタクトフォームや、コメント投稿、ユーザー登録などの画面に設置して、そのボタンを押すのが人間なのかどうかを判定します。人間でないと判断した場合は、エラーを出すなどして実行できないようにします。
v2 までは、画像などの認証でしたが、v3からは、ページにおけるユーザーの動きを見て、その判定を自動で行うので、ユーザーにとっては何も行う必要もなく負担をかけることもありません。
Ajax を使って reCAPTCHA 認証する
書いてみると意外と簡単ですが、これを行うには、reCAPTCHA 自体の仕様というか、全体の動きを把握してないとコードに落とし込みにくいと思います。
- 認証が必要なページにGoogle指定のJavaScriptファイルを読み込む
- サイトキーを渡して、Googleのスクリプトでtokenを生成する
- token を submit などのタイミングでPOST送信する
- 自分のサイト上で受け取った token をGoogleのURLへ「シークレットキー」と一緒に投げる
- 認証可否が返ってくる(success: true, または、score: 0〜1など)
- その認証可否を参考にしてサーバ上で処理分けする
以上が、だいたいの流れになります。
これを理解した上で、順にコードに落とし込んでいきます。
「サイトキー」と「シークレットキー」を取得する
reCAPTCHA を使用するには、Googleアカウントが必要です。ログインした上で、それぞれのキーを取得するには、以下にアクセスします。
これらのキーはいわゆる公開鍵のようなイメージでしょうか。その名のとおり「サイトキー」は、HTML 上で確認できるように記述します(ユーザーに見える必要はないですが、覗こうと思えば覗けるという意味です)。その一方で「シークレットキー」は、秘密鍵として自身のサーバ上に記述します。
認証が必要なページに Google 指定の JavaScript ファイルを読み込む
公式ドキュメント
https://developers.google.com/recaptcha/docs/v3
にもありますが、必要なページの「</body>」タグの手前付近で、Google指定のJavaScriptファイルを記述します。
<script src="https://www.google.com/recaptcha/api.js?render=サイトキー"></script>
サイトキーを渡して、Google のスクリプトで token を生成する
次に、あらかじめGoogleスクリプトによって、token を生成してどこかに保持しておく必要があります。
もし <form>
タグを作っているようなら、
<input type="hidden" name="recaptcha_response" id="recaptchaResponse">
と、hidden 属性の input タグに埋め込んでしまっても良いでしょう。HTML ファイルが読み込まれると同時に、token を生成して、このタグに格納します。
$(document).ready(function() {
grecaptcha.ready(function () {
grecaptcha.execute('サイトキー', { action: 'contact' }).then(function (token) {
var recaptchaResponse = $('#recaptchaResponse');
recaptchaResponse.value = token;
});
});
});
ただ、今回は、Ajax を使って POST するので、グローバル変数に格納することで留めておきましょう。
// グローバル変数
var tokenData;
$(document).ready(function() {
// Google reCAPTCHA v3 のトークンを生成
grecaptcha.ready(function () {
grecaptcha.execute('サイトキー', { action: 'submit' }).then(function (token) {
tokenData = token;
});
});
});
なお、action
に入力される文字列ですが、ここは書き分けることで、admin consoleから「リクエスト数」として集計・参照することができます。ですので、あまり集計や、後の参照を気にする必要がない場合は、文字列はなんであっても良いということです。
token を submit などのタイミングで POST 送信する
そして、上記で保存したグローバル変数を Ajax の POST 値に載せて送信します。
function sendData() {
$.ajax({
type: "POST",
url: "register_submit.php",
timeout: 10000,
cache: false,
data: {
'mail': "m@hibara.org",
'password': "password123",
'recaptchaResponse': tokenData
},
dataType: 'text'
})
.done(function (data) {
// "OK"
location.href = "register_complete.php";
return true;
})
.fail(function () {
alert("サーバー内でエラーがあったか、サーバーから応答がありませんでした。");
return false;
})
.always(function (data_or_jqXHR, textStatus, jqXHR_or_errorThrown) {
});
}
自分のサイト上で受け取った token を Google の URL へ「シークレットキー」と一緒に投げる
投げた先、今回上記の例で言うと register_submit.php
の中身はこう書きます。
if(isset($_POST['recaptchaResponse']) && !empty($_POST['recaptchaResponse'])) {
$secret = 'シークレットキー';
//get verify response data
$verifyResponse = file_get_contents('https://www.google.com/recaptcha/api/siteverify?secret='.$secret.'&response='.$_POST['recaptchaResponse']);
$responseData = json_decode($verifyResponse);
if ( $responseData->success ) {
if ( $responseData->score < 0.8 ) { // おまえ人間じゃねえ!
// 認証スコアが低い
exit();
}
}
else {
// 認証失敗
exit();
}
}
else {
// POST値が正常に投げられてこなかった
exit();
}
// ここまで抜けてきたとき、正常処理を行う
ここのスコア $responseData->score
は、0〜1 の間を取ります。1に近いほど人間と判断できます。私が何回か試行してみた実感としては、ほぼ 0.9
以上のスコアを叩くような気がします。この値については、サイトによって試行錯誤する必要があるかもしれません。
ここで得られる他の値、結果については、先に挙げた公式ドキュメントにあるとおりです。
{
"success": true|false, // whether this request was a valid reCAPTCHA token for your site
"score": number // the score for this request (0.0 - 1.0)
"action": string // the action name for this request (important to verify)
"challenge_ts": timestamp, // timestamp of the challenge load (ISO format yyyy-MM-dd'T'HH:mm:ssZZ)
"hostname": string, // the hostname of the site where the reCAPTCHA was solved
"error-codes": [...] // optional
}
JSONでの例になっていますが、この仕様に沿って、上記のPHPファイル内で処理すれば問題なく取得できるでしょう。
まとめ
とりあえず、Ajaxで reCAPTCHA v3 を実装した例が少なく、仕様全体のイメージもしにくかったので記事にしてみました。「最初にトークンを生成して投げる」ということを知れば、あとはいくらでも応用が利くとは思います。