PHPでreCAPTCHA(v2)を使ってみた

PHP

PHPでreCAPTCHA(v2)を使ってみた

reCAPTCHAのバージョン2を某システムに実装することがありました。
実装前に検証を行ったので、内容をまとめてみました。

本記事ではreCAPTCHA(v2) APIをPHPで呼び出すときのサンプルコードと解説を載せています。
サイトキーやリクエストキーの取得方法については、こちらの記事を確認してください。
404 NOT FOUND | コードライク
福岡でWebシステム開発中心に活動中のフリーランスプログラマのブログ。PHP/JavaScript/C#/Go/Javaなど色々やってます!

公式ドキュメントはこちらです。

HTMLコードのサンプル

reCAPTCHAを検証するサンプルとして、下記のログインフォームを用意しました。

コード

上記で表示されている画像ですが、HTMLのコードは、このように記載しました。

<html>
  <head>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
  </head>
  <body>
    <nav class="navbar navbar-dark bg-primary">
      <a class="navbar-brand" href="#">reCapture v2 Test</a>
    </nav>
    <div class="container mt-3">
      <h3>ログインフォーム(reCapture v2 Test)</h3>
      <form method="post" action="recaptcha_post.php">
        <div class="form-group">
          <label for="email">メールアドレス</label>
          <input type="email" id="email" name="email" class="form-control" />
        </div>

        <div class="form-group">
          <label for="password">パスワード</label>
          <input id="password" type="password" name="password" class="form-control" />
        </div>

        <button type="submit" class="btn btn-primary">送信</button>
        <div class="g-recaptcha mt-3" data-sitekey="[reCaptchaの管理画面で取得した*サイトキー*]" data-callback="callback_recaptcha"></div>
      </form>
    </div>
    <script src="https://www.google.com/recaptcha/api.js" async defer></script>
    <script>
      function callback_recaptcha(token) {
        console.log(token);
        alert("click recaptcha: "+token); 
      }
    </script>
  </body>
</html>

解説

bootstrap4を使ってパパッと作ってみました。
ログインフォームで、reCAPTCHAを使う場合を想定しました。

reCAPTCHA固有のタグとスクリプトに注目すると
まず、reCAPTCHAを使用するために、下記のスクリプトを読み込む必要があります。

<script src="https://www.google.com/recaptcha/api.js" async defer></script>

そして、下記のタグを入れておくことで、reCAPTCHAの入力フォームが表示されます。
今回はformタグ配下に入れています。

 <div class="g-recaptcha mt-3" data-sitekey="[reCaptchaの管理画面で取得した*サイトキー*]" data-callback="callback_recaptcha"></div>

classのg-recaptchaは必須です。ない場合は表示されなくなります。
data-sitekey属性(「reCAPTCHAの管理画面で取得したサイトキー」の箇所)には、reCAPTCHAの管理画面で取得したサイトキーを入力します。
data-callback属性の箇所には、チェックを入れたときに実行するjavascriptの関数を記載します。

チェックされると、下記の関数が実行されます。

function callback_recaptcha(token) {
  console.log(token);
  alert("click recaptcha: "+token); 
}

引数でrecaptchaのapiで使用するトークンが取得できるので、alertを使って表示してみました。

今回は下記のようにformタグでrecaptcha_post.phpにPOSTするようにしましたが

<form method="post" action="recaptcha_post.php">

SPAなど、ajaxを使う場合はcallbackされたtokenを使うようにすると良さそうです。
今回はformタグの中にreCAPTCHAのタグを入れているので、フォームが送信された場合には、g-recaptcha-responseという名前でトークンが送信されます。
トークンは1回しか使用できません。2回目以降で同じトークンを使用すると、reCAPTCHA APIからtimeout-or-duplicateのエラーが返ってきます。

phpでreCAPTCHA(v2) APIを実行するサンプル

postされた時にreCAPTCHA APIにリクエストする検証用のPHPファイルを下記のように作成しました。

コード

<?php

echo "<pre><code>";
var_dump($_POST);
echo "</code></pre>";

//reCAPTCHA APIへのリクエストURL 
$url = 'https://www.google.com/recaptcha/api/siteverify';
$secretKey = "[reCaptchaの管理画面で取得した*シークレットキー*]";

$request = [
  'secret' => $secretKey,
  'response' =>  $_POST['g-recaptcha-response']
];

$context = [
  'http' => array[
    'method'  => 'POST',
    'header'  => 'Content-Type: application/x-www-form-urlencoded',
    'content' => http_build_query($request)
  ]
];

$context =  stream_context_create($context);
$apiResponse = file_get_contents($url, false, $context);

// reCAPTCHA APIの返却値出力
echo "<pre><code>";
var_dump($apiResponse);
echo "</code></pre>";

解説

最初に送信されたデータが確認できるように、$_POSTを画面に出力するようにしました。
次にreCAPTCHA APIにリクエストするためのurlをとシークレットキーを定義して、それぞれ値を格納しています。
シークレットキーもreCAPTCHAの管理画面から取得できます。

シークレットキーと、画面から送信されてきたトークンを使用して$requestに配列化して格納しています。
そして、$contextにreCAPTCHA APIにリクエストするための情報を配列として格納しています。

最後にfile_get_contents関数を使用して、reCAPTCHA APIにリクエストを送信します。
送信した後は結果が表示されるようにvar_dump関数を使用してみました。

動作確認

フォームが表示された後に、入力してチェックしてみます。

チェックを入れると、このようにトークンが取得できることが確認できました。(javascriptのcallback_recaptcha関数)

何度か試していると、このように悪意のあるアクセスでないかチェックするポップアップが出ます。

フォームを送信すると、それぞれ入力した値と、reCAPTCHAのトークンが表示されました。

reCAPTCHA APIの応答内容を確認する

reCAPTCHA APIから返ってきた値はJSONの文字列になっています。

string(90) "{
  "success": true,
  "challenge_ts": "2021-01-07T04:30:39Z",
  "hostname": "localhost"
}"

それぞれ下記のような内容になるようです。
エラーコードはエラーだった場合についてきます。

項目 内容
success 成功した場合はtrue/失敗した場合はfalse
challenge_ts 実行時のタイムスタンプ
hostname reCAPTCHAを実行したホスト名
error-codes エラーコード

返ってきたステータスで、処理を分岐するには下記のようにjson_decode関数で配列に戻してから分岐すると良さそうですね。
問題ない場合はsuccessがtrueになります。

$apiResponse = json_decode($apiResponse);
if ($apiResponse->success) {
  // TODO:成功時の処理...
} else {
  // TODO:失敗時の処理...
}

エラーコードの内容について

公式のドキュメントに書いてありますが、下記の意味になります。

エラーコード 内容
missing-input-secret シークレットパラメータがありません。
invalid-input-secret シークレットパラメータが無効または不正な形式です。
missing-input-response リクエストパラメーターがありません。
invalid-input-response リクエストパラメータが無効であるか、形式が正しくありません。
bad-request リクエストが無効または不正な形式です。
timeout-or-duplicate 応答は無効になりました。古すぎるか、以前に使用されたことがあります。

コメント

  1. 浅野 より:

    リキャプチャの実装をよく学ぶことができました。とてもありがたいです。どうもありがとうございました。

タイトルとURLをコピーしました