LoginSignup
2

More than 1 year has passed since last update.

posted at

updated at

Google reCAPTCHA v3はどうするとスパム判定されるのか?

思うところがあって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する。

image.png

{
  "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でやる場合、ライブラリを使うのが良さそうですね。
  • 登場人物は多い。駆け出しエンジニアには難しいかも知れない。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
2