6
1

More than 1 year has passed since last update.

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

Last updated at Posted at 2019-08-16

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

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
  3. You can use dark theme
What you can do with signing up
6
1