思うところがあってreCAPTCHA v3を触ってみたく、試してみました。
v2も合わせて読みたい。
https://qiita.com/kaibadash@github/items/2681f02ef16ad6421da1
Google reCAPTCHA v3
いわゆる 「ロボットではありません」
は誰もが触ったことがあると思います。
それが表示されるのは Google reCAPTCHA v2 で、v3はそれが表示されないのですが、v3が新しくてよい、というわけではなく以下のような区別のようです。
- v2: スパムの判定をGoogleに任せる
- v3: スパムの判定を自分で行い、細かい制御を行う
- ハンドリングしてあとから制御したりできるメリットが有る
- ロボットじゃないかもしれないしね
今回、ターゲットになるフォーム
僕の実験サイトなので好きに触ってみてください。
よくあるお問い合わせフォームになります。
form名もcontactで悪いbotさんに拾ってもらえそう?
key, secretの発行
https://www.google.com/recaptcha/admin/create
ここからkeyとsecretを発行します。
keyはフロントエンドに埋め込むので誰に見られても良いものですがsecretはその名の通り秘密にすべきもので、バックエンドで使います。
作成済みプロジェクトは以下から、「v3 Admin Console」からkey, secretを見られる。
一覧で見せて欲しいですね。
フロントエンド
Googleにkeyを渡し、tokenを受け取り、バックエンドにわたす必要があります。
<input type="hidden" name="recaptcha" id="recaptcha" />
hiddenパラメータを用意しました。
<script src="https://www.google.com/recaptcha/api.js?render=[key]"></script>
recaptchaのスクリプトにkeyを渡して読み込みます。
[key]
には取得したkeyを入れてくださいね。
grecaptcha.ready(function() {
grecaptcha.execute('[key]', {action: 'homepage'}).then(function(token) {
var recaptcha = document.getElementById('recaptcha');
recaptcha.value = token;
});
});
[key]
には取得したkeyを入れてくださいね。
grecaptcha.execute
を呼ぶとtokenが得られますので、上記のhiddenパラメータに入力しておきます。
actionはformのactionではなく、Googleの指定するactionで、 homepage
, login
, social
, e-commerce
のいずれかを指定します。
名前からある程度わかりますが、詳しくはドキュメントを参照ください。
バックエンド
verify の結果を表示しします。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>reCAPTCHA</title>
</head>
<body>
<?php
if (isset($_POST["recaptcha"]) && !empty($_POST["recaptcha"])) {
$secret = "SECRET"; // TODO:要変更
$verifyResponse = file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret=".$secret."&response=".$_POST["recaptcha"]);
echo $verifyResponse;
} else {
echo "invalid request: recaptcha is not found.";
}
?>
</html>
試してみた
正常系
{
"success": true,
"challenge_ts": "2019-08-16T14:21:18Z",
"hostname": "pokosho.com",
"score": 0.9,
"action": "homepage"
}
高いスコアです。
JSでクリックしてみる
Chrome Developer Toolを立ち上げ、consoleからJSでクリックしてみる。
document.getElementById("submit").click();
{
"success": true,
"challenge_ts": "2019-08-16T14:21:40Z",
"hostname": "pokosho.com",
"score": 0.9,
"action": "homepage"
}
なんだと?
v2の「私はロボットではありません」に対してこれをやると怒られたのですが…!
ブックマークレットとかも使えそうですね。
同じリクエストを飛ばしてみる
Chrome Developer ToolからCopy as cURLする。
{
"success": false,
"error-codes": [
"timeout-or-duplicate"
]
}
おお〜。
headless chrome(puppeteer)
puppeteer で操作してみる。
乱暴にいきなりsubmitを押す、という乱暴な操作です。
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://pokosho.com/a/recaptcha/',
{'timeout': 10000, 'waitUntil':'load'}
);
await page.waitForTimeout(1000);
await page.click('#submit');
const json = await page.content();
console.log(json);
await browser.close();
})();
$ node index.js
<!DOCTYPE html><html><head>
<meta charset="utf-8">
<title>reCAPTCHA</title>
</head>
<body>
{
"success": true,
"challenge_ts": "2022-02-20T12:35:21Z",
"hostname": "pokosho.com",
"score": 0.9,
"action": "homepage"
}
</body></html>
あれ…? puppeteerは人間だった…?
ガードしたい対象が違うのか?
まとめ
- 「私はロボットではありません」の印象が強すぎて「UXを損ねるのでは?」という懸念が挙がるがそんなことはない
- JS読み込んで終わり、というわけにはいかない。botかどうかの判定はバックエンドで行う。
- Googleからドメインを指定してkeyとsecretを発行する。
- keyはフロントエンドからGoogleに渡す。するとtokenが得られる。
- tokenをバックエンドに渡す。
- バックエンドはtokenとsecretをGoogleに渡す。すると判定結果が得られる。
- 判定結果はbool値の
success
のパラメータ見れば良い。 - ReactやVueでやる場合、ライブラリを使うのが良さそうですね。
- 登場人物は多い。駆け出しエンジニアには難しいかも知れない。