PHP による Google reCAPTCHA v2 を用いた検証の例

このページは、PHP で Google reCAPTCHA v2を用いた検証を行う例を記載したページです。

注意

  • コードのライセンスは CC0 (クレジット表示不要、改変可、商用可) です。
  • 実際に使用する場合には reCAPTCHA に加えて別のデータ (ログインフォームならユーザーIDとパスワード) の確認も必要になるはずですが、reCAPTCHA の試験を目的としたコードとして記載しているためそれらは省いています。

コード (cURLを使用した単純な例)

<?php
define('RECAPTCHA_SITE_KEY', ''); // reCAPTCHAのサイトキー (Google Developer Consoleから取得したものをセットしてください)
define('RECAPTCHA_SECRET_KEY', ''); // reCAPTCHAのシークレットキー (同上)

/**
 * reCAPTCHA 検証を行います。(単純な検証)
 *
 * @param string $gRecapchaResponse  $_POST['g-recaptcha-response'] 等から取得できるデータ
 * @return array 'success' (成功した場合 true), 'message' (結果メッセージ), 'response' (reCAPTCHA APIからの応答データ(stdClass)) のキーからなる array
 */
function recaptchaSimple($gRecapchaResponse) {
    $success = false; // reCAPTCHA が成功した場合 true, それ以外の場合 false
    $message = ''; // 結果メッセージ
    $response = null; // reCAPTCHA応答データ

    // reCAPTCHAデータあり
    if ($gRecapchaResponse) {
        // reCAPTCHA API用送信データ
        $data = array(
            'secret' => RECAPTCHA_SECRET_KEY,
            'response' => $gRecapchaResponse
        );

        // cURL から応答データを取得 (cURLを使用できない場合は正常に動作しません)
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, "https://www.google.com/recaptcha/api/siteverify");
        curl_setopt($curl, CURLOPT_POST, true); // POST送信
        curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data, '', '&', PHP_QUERY_RFC3986)); // 送信データをセット
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // 証明書検証をしない (環境によって動作しない場合があるため)
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); // curl_exec() 経由で応答データを直接取得できるようにする
        $responseString = curl_exec($curl); // 応答データ取得
        curl_close($curl);

        // 応答取得完了
        if ($responseString !== false) {
            $response = json_decode($responseString, true);
            // JSON解析完了
            if ($response !== false) {
                // reCAPTCHA検証成功
                if (isset($response['success']) && $response['success']) {
                    $success = true;
                    $message = 'reCAPTCHAの検証に成功しました。';
                }
            }
        }
    }

    // 失敗
    if (!$success) {
        $message = 'reCAPTCHAの検証に失敗しました。';
    }

    return compact('success', 'message', 'response');
}
function h($value, $encoding = 'UTF-8') { return htmlspecialchars($value, ENT_QUOTES, $encoding); } // HTMlエスケープ出力用
function eh($value, $encoding = 'UTF-8') { echo h($value, $encoding); } // 同上


// reCAPTCHA 検証 (サンプルフォームを POST にしているため POST 時に検証)
if (strtoupper($_SERVER['REQUEST_METHOD']) == 'POST') {
    $gRecapchaResponse = filter_input(INPUT_POST, 'g-recaptcha-response'); // reCAPTCHAデータ ( $_POST['g-recaptcha-response'] 等でもよいです )
    $result = recaptchaSimple($gRecapchaResponse); // reCAPTCHA 検証
}

// https://developers.google.com/recaptcha/docs/display#auto_render をベースに調整しています。
?>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>reCAPTCHA demo: Simple page</title>
        <script src="https://www.google.com/recaptcha/api.js" async defer></script>
    </head>
    <body>
        <form method="POST">
            <!-- reCAPTCHAが表示されるエリアです。Developer Consoleで取得したサイトキーを設定します -->
            <div class="g-recaptcha" data-sitekey="<?php eh(RECAPTCHA_SITE_KEY) ?>"></div>
            <br/>
            <input type="submit" value="送信">
        </form>

        <!-- 結果データの確認 -->
        <?php if (isset($result['message'])) { ?>
            <p><?php eh($result['message']) ?></p>
        <?php } ?>
        <?php if (isset($result['response'])) { ?>
            <pre class="result"><?php var_dump($result['response']) ?></pre>
        <?php } ?>
    </body>
</html>

コード (cURL, file_get_contents() 両対応 + メッセージを詳細にした処理)

<?php
define('RECAPTCHA_SITE_KEY', ''); // reCAPTCHAのサイトキー (Google Developer Consoleから取得したものをセットしてください)
define('RECAPTCHA_SECRET_KEY', ''); // reCAPTCHAのシークレットキー (同上)

/**
 * reCAPTCHA 検証を行います。
 *
 * @param string $gRecapchaResponse  $_POST['g-recaptcha-response'] 等から取得できるデータ
 * @return array 'success' (成功した場合 true), 'message' (結果メッセージ), 'response' (reCAPTCHA APIからの応答データ(stdClass)) のキーからなる array
 */
function recaptcha($gRecapchaResponse) {
    $success = false; // reCAPTCHA が成功した場合 true, それ以外の場合 false
    $message = ''; // 結果メッセージ
    $response = null; // reCAPTCHA応答データ

    // reCAPTCHAデータあり
    if ($gRecapchaResponse) {
        // reCAPTCHA API用送信データ
        $data = array(
            'secret' => RECAPTCHA_SECRET_KEY,
            'response' => $gRecapchaResponse
        );

        // cURL が使用できる場合 cURL から応答データを取得
        if (function_exists('curl_version')) {
            $curl = curl_init();
            curl_setopt($curl, CURLOPT_URL, "https://www.google.com/recaptcha/api/siteverify");
            curl_setopt($curl, CURLOPT_POST, true); // POST送信
            curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data, '', '&', PHP_QUERY_RFC3986)); // 送信データをセット
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // 証明書検証をしない (環境によって動作しない場合があるため)
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); // curl_exec() 経由で応答データを直接取得できるようにする
            $responseString = curl_exec($curl); // 応答データ取得
            curl_close($curl);
        }
        // allow_url_fopen が 1 の場合 file_get_contents() から応答データを取得
        else if (ini_get('allow_url_fopen') === '1') {
            $context = stream_context_create(array(
                'http' => array(
                    'method'  => 'POST', // POST送信
                    'header'  => 'Content-Type: application/x-www-form-urlencoded',
                    'content' => http_build_query($data, '', '&', PHP_QUERY_RFC3986) // 送信データをセット
                ),
                'ssl' => array(
                    'verify_peer'      => false, // 証明書検証をしない (環境によって動作しない場合があるため)
                    'verify_peer_name' => false, // ピア名検証をしない (同上)
                )
            ));
            $responseString = @file_get_contents('https://www.google.com/recaptcha/api/siteverify', false, $context); // 応答データ取得
        }
        else {
            // どちらも使えない場合はサーバー環境の見直しが必要かもしれません…
            $message = 'reCAPTCHA APIに接続できませんでした。';
        }

        // 応答取得完了
        if ($responseString !== false) {
            $response = json_decode($responseString, true);
            // JSON解析完了
            if ($response !== false) {
                // reCAPTCHA検証成功
                if (isset($response['success']) && $response['success']) {
                    $success = true;
                    $message = 'reCAPTCHAの検証に成功しました。';
                }
                // 失敗
                else {
                    $message = 'reCAPTCHAの検証に失敗しました。';

                    if (isset($response['error-codes']) && is_array($response['error-codes'])) {
                        $messageArray = array();
                        foreach ($response['error-codes'] as $code) {
                            switch ($code) {
                                case 'missing-input-secret':
                                    $messageArray[] = 'シークレットキーが指定されていません。';
                                    break;
                                case 'invalid-input-secret':
                                    $messageArray[] = '有効なシークレットキーではありません。';
                                    break;
                                case 'missing-input-response':
                                    $messageArray[] = 'reCAPTCHAの入力がされていません。';
                                    break;
                                case 'invalid-input-response':
                                    $messageArray[] = '有効なreCAPTCHAの入力ではありません。';
                                    break;
                                case 'bad-request':
                                    $messageArray[] = '不正なリクエストです。';
                                    break;
                                case 'timeout-or-duplicate':
                                    $messageArray[] = '検証に時間がかかりすぎているか、検証データが重複しています。';
                                    break;
                            }
                        }
                        if ($messageArray) $message .= implode('', $messageArray);
                    }
                }
            }
            else {
                $message = 'reCAPTCHAの応答データが正常に取得できませんでした。';
            }
        }
        else if (!$message) {
            $message = 'reCAPTCHAの応答データが取得できませんでした。';
        }
    }
    else {
        $message = 'reCAPTCHAの検証に失敗しました。reCAPTCHAの入力がされていません。';
    }

    return compact('success', 'message', 'response');
}
function h($value, $encoding = 'UTF-8') { return htmlspecialchars($value, ENT_QUOTES, $encoding); } // HTMlエスケープ出力用
function eh($value, $encoding = 'UTF-8') { echo h($value, $encoding); } // 同上


// reCAPTCHA 検証 (サンプルフォームを POST にしているため POST 時に検証)
if (strtoupper($_SERVER['REQUEST_METHOD']) == 'POST') {
    $gRecapchaResponse = filter_input(INPUT_POST, 'g-recaptcha-response'); // reCAPTCHAデータ ( $_POST['g-recaptcha-response'] でもよいです )
    $result = recaptcha($gRecapchaResponse); // reCAPTCHA 検証
}

// https://developers.google.com/recaptcha/docs/display#auto_render をベースに調整しています。
?>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>reCAPTCHA demo: Simple page</title>
        <script src="https://www.google.com/recaptcha/api.js" async defer></script>
    </head>
    <body>
        <form method="POST">
            <!-- reCAPTCHAが表示されるエリアです。Developer Consoleで取得したサイトキーを設定します -->
            <div class="g-recaptcha" data-sitekey="<?php eh(RECAPTCHA_SITE_KEY) ?>"></div>
            <br/>
            <input type="submit" value="送信">
        </form>

        <!-- 結果データの確認 -->
        <?php if (isset($result['message'])) { ?>
            <p><?php eh($result['message']) ?></p>
        <?php } ?>
        <?php if (isset($result['response'])) { ?>
            <pre class="result"><?php var_dump($result['response']) ?></pre>
        <?php } ?>
    </body>
</html>

結果

※下記は画像のためクリックしても動作しません。

結果

参考