htmlcss reCAPTCHA v3 送信時にトークンを取得

2020年3月22日

reCAPTCHA v3 を使う際に、送信時にトークンを取得する方法についての覚書です。

reCAPTCHA v3 のガイドに掲載されている以下の例の場合、ページを読み込んですぐにトークンを取得しているので、2分後にはこのトークンの有効期限が切れてしまいます。

<script src="https://www.google.com/recaptcha/api.js?render=_reCAPTCHA_site_key"></script>
<script>
//ページ読み込み時にトークンを取得
grecaptcha.ready(function() {
    grecaptcha.execute('_reCAPTCHA_site_key_', {action: 'homepage'}).then(function(token) {
       ...
    });
});
</script>

以下は、送信ボタンをクリックした際にトークンを取得する例で、トークンの有効期限は送信ボタンをクリックしてから2分間になります。

以下では jQuery を使ってフォーム要素にイベントハンドラを設定し、送信時に event.preventDefault() で送信を停止してトークンを取得しています。

<html lang="ja">
<head>
<title>reCAPTCHA v3 送信時にトークンを取得するサンプル</title>
</head>
<body>
  <h1>reCAPTCHA v3 sample (onsubmit)</h1>
  <form id="rc_form" method="post">
    <button type="submit">送信</button>
  </form>
<script src="https://www.google.com/recaptcha/api.js?render=サイトキー"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
jQuery(function($){
  $('#rc_form').submit(function(event) {
    event.preventDefault(); //デフォルトの動作(送信)を停止
    var action_name = 'submit_sample'; //アクション名 
    grecaptcha.ready(function() {
      grecaptcha.execute('サイトキー', { action: action_name }).then(function(token) {
        $('#rc_form').prepend('<input type="hidden" name="g-recaptcha-response" value="' + token + '">');
        $('#rc_form').prepend('<input type="hidden" name="action" value="' + action_name + '">');
        $('#rc_form').unbind('submit').submit(); //submit() を実行
      });;
    });
  });
})
</script>
</body>
</html>

以下は PHP を使った検証(アクション名の確認とスコアが0.5以上の場合に認証)も含めたコードです。

検証を通過すると、その判定結果とスコア及び API のレスポンスを表示します。

実際の使用では検証を通過するとメールの送信などを実行します。

<?php
// サイトキーとシークレットキーを記述したファイルの読み込み
require 'libs/recaptcha_vars.php';
// reCAPTCHA サイトキーを変数に格納
$siteKey = V3_SITEKEY;
// reCAPTCHA シークレットキーを変数に格納
$secretKey = V3_SECRETKEY;
//reCAPTCHA トークン
$token = isset( $_POST[ 'g-recaptcha-response' ] ) ? $_POST[ 'g-recaptcha-response' ] : NULL;
//reCAPTCHA アクション名 
$action = isset( $_POST[ 'action' ] ) ? $_POST[ 'action' ] : NULL;

$result_status = ''; // 結果を表示する文字列を初期化

if ( $token && $action) { // トークンとアクション名が取得できれば
  //cURL セッションを初期化(API リクエストとレスポンスの取得)
  $ch = curl_init();
  // curl_setopt() により転送時のオプションを設定
  //URL の指定
  curl_setopt($ch, CURLOPT_URL,"https://www.google.com/recaptcha/api/siteverify");
  //HTTP POST メソッドを使う
  curl_setopt($ch, CURLOPT_POST, true );
  //API パラメータの指定
  curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array(
    'secret' => $secretKey, //シークレットキー
    'response' => $token  //トークン
  )));
  //curl_execの返り値を文字列にする
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  //転送を実行してレスポンスを $api_response に格納
  $api_response = curl_exec($ch);
  //セッションを終了
  curl_close($ch);
  
  //レスポンスの $json(JSON形式)をデコード
  $result = json_decode( $api_response );
  
  //判定
  if ( $result->success && $result->action === $action && $result->score >= 0.5) {
    //success が true でアクション名が一致し、スコアが 0.5 以上の場合は合格
    $result_status = '合格: $result->score : ' . $result->score;
    // 合格した場合の処理(メールの送信など)を実行
  } else {
    // 上記以外の場合は 不合格
    $result_status = '不合格';
    // 不合格の場合の処理(エラーを表示するなど)を実行
  }
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<title>reCAPTCHA v3 送信時にトークンを取得するサンプル</title>
</head>
<body>
  <h1>reCAPTCHA v3 sample (onsubmit)</h1>
  <form id="rc_form" method="post">
    <button type="submit">送信</button>
  </form>
  <div>
    <p>[検証結果]</p>
    <p><?php echo $result_status; ?></p>
    <p>[var_dump($resp)]</p>
    <pre><?php if(isset($api_response)) {var_dump($api_response);} ?></pre>
  </div>
<script src="https://www.google.com/recaptcha/api.js?render=<?php echo $siteKey; ?>"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
jQuery(function($){
  $('#rc_form').submit(function(event) {
    event.preventDefault();
    var action_name = 'submit_sample'; //アクション名 
    grecaptcha.ready(function() {
      grecaptcha.execute('<?php echo $siteKey; ?>', { action: action_name }).then(function(token) {
        $('#rc_form').prepend('<input type="hidden" name="g-recaptcha-response" value="' + token + '">');
        $('#rc_form').prepend('<input type="hidden" name="action" value="' + action_name + '">');
        $('#rc_form').unbind('submit').submit();
      });;
    });
  });
})
</script>
</body>
</html>

上記の例では API リクエストとレスポンスの取得(16行目〜33行目)には cURL関数を使用していますが、もし file_get_contents() を使う場合は以下のようになります。

//API Request URL 
$url = 'https://www.google.com/recaptcha/api/siteverify';
//パラメータを指定
$data = array(
  'secret' => $secretKey,   //シークレットキー
  'response' =>  $_POST[ 'g-recaptcha-response' ] //トークン
);
$context = array(
  'http' => array(
    'method'  => 'POST', // POST メソッドを使う
    'header'  => implode("\r\n", array('Content-Type: application/x-www-form-urlencoded',)),
    'content' => http_build_query($data)
  )
);
//上記パラメータを指定して file_get_contents でレスポンスを取得
$api_response = file_get_contents($url, false, stream_context_create($context));

サンプルページ

詳細は以下を御覧ください。

Google reCAPTCHA の使い方(v2/v3)