ミギムキ

[JavaScript] reCAPTCHAのサーバー認証処理をメールプログラムに手を入れず行いたい

Google reCAPTCHAからキーを取得

サンプルコード

html

<form name="gr_form"> <div id="gr_checkbox"></div> <input type="button" value="送信" disabled="disabled" id="gr_submit" onclick="trySubmit();" /> </form>
  • 適宜、フォームの中にinputタグなどを追加してください
  • 「gr_checkbox」のIDをつけた要素が「私はロボットではありません」のチェックボックスになります
  • フォームにはあえてaction属性を指定していません。認証が完了した際、JavaScriptで動的にaction属性を設定します
  • 送信ボタンには「disabled」属性を設定して無効状態にしています。後述するJavaScriptの処理で、「私はロボットでは〜」にチェックがされたとき「disabled」を解除します

CSS

#gr_checkbox { display: table; margin: 10px auto; } #gr_submit:disabled { pointer-events: none; opacity: 0.5; }
  • 「#gr_submit」は送信ボタンに設定したIDです。送信ボタンがdisabledのとき、操作を禁止してボタンを透過します

JavaScript

<script src="https://www.google.com/recaptcha/api.js?onload=grOnloadCallback&render=explicit" async defer></script> <script type="text/javascript"> var grOnloadCallback = function() { grecaptcha.render('gr_checkbox', { 'sitekey' : 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'callback' : grCallback, 'expired-callback' : grExpiredCallback }); }; var grCallback = function(response) { document.getElementById('gr_submit').removeAttribute('disabled'); }; var grExpiredCallback = function(response) { document.getElementById('gr_submit').setAttribute('disabled', 'disabled'); }; var trySubmit = function() { if( document.gr_form.reportValidity() ) { var xhr = new XMLHttpRequest(); xhr.open('POST', '/gr_check.php', true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.onreadystatechange = function() { if(xhr.readyState === 4 && xhr.status === 200) { if('' != xhr.responseText) { document.gr_form.action = xhr.responseText; document.gr_form.method = 'POST'; document.gr_form.submit(); } } } var gr_response = document.getElementById('g-recaptcha-response').value; xhr.send('response=' + gr_response); } } </script>
  • JavaScriptの処理は大まかに2つ。「私はロボットでは〜」のチェックボックス関連の処理と、送信ボタンを押したときのサーバーサイドの認証処理となります
  • 以下、部分的に抜粋して解説します
var grOnloadCallback = function() { grecaptcha.render('gr_checkbox', { 'sitekey' : 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'callback' : grCallback, 'expired-callback' : grExpiredCallback }); };
  • チェックボックス関連の処理です。画面表示時にGoogle側からこの関数が呼ばれ、チェックボックスの描画やチェック時の処理が設定されます
  • 「gr_checkbox」がチェックボックスを描画(レンダリング)させたい要素を指すIDになっています
  • 「sitekey」には、Googleから取得したサイトキーを設定します
  • 「callback」には、チェックボックスがチェックされた後に呼んでもらいたい関数を指定します
  • 「expired-callback」には、チェック後のタイムアウト時に呼んでもらいたい関数を指定します。リキャプチャは、一度チェックをした後に2分が経過するとタイムアウトとなり、認証処理がキャンセルされます。タイムアウトしたときに必要な処理を行うためにここで関数を指定します
var grCallback = function(response) { document.getElementById('gr_submit').removeAttribute('disabled'); };
  • チェックボックスにチェックがされたときに呼ばれる関数です
  • デフォルトで「disabled」状態だった送信ボタンを有効にして押せるようにします
var grExpiredCallback = function(response) { document.getElementById('gr_submit').setAttribute('disabled', 'disabled'); };
  • チェック後のタイムアウト時に呼ばれる関数です
  • チェック時に一度有効化した送信ボタンを再度無効化し、ユーザーがもう一度チェックボックスにチェックをしないと送信できないようにします
var trySubmit = function() { if( document.gr_form.reportValidity() ) { var xhr = new XMLHttpRequest(); xhr.open('POST', '/gr_check.php', true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.onreadystatechange = function() { if(xhr.readyState === 4 && xhr.status === 200) { if('' != xhr.responseText) { document.gr_form.action = xhr.responseText; document.gr_form.method = 'POST'; document.gr_form.submit(); } } } var gr_response = document.getElementById('g-recaptcha-response').value; xhr.send('response=' + gr_response); } }
  • 今回の肝となるサーバーサイドの認証処理になります
  • リキャプチャは、チェックボックスによる認証とGoogle側へのリクエスト認証の二重のチェック処理で実現します
  • チェックボックスにチェックを入れると、Googleからレスポンスキーを受け取ります。このレスポンスキーと、シークレットキーをGoogleの認証サーバーにリクエスト送信することで、きちんとチェックボックスの操作を経た送信であることを保証します
  • 本来は、このリクエスト処理をメールプログラム内に組み込むことになりますが、今回はリクエスト処理をJavaScriptで行うことで、サーバーサイドのメールプログラムに手を入れずに済むようにしています
  • リクエスト処理は「XMLHttpRequest」を使用し、Googleと認証のやり取りを行う「gr_check.php」を呼び出します
  • 認証に成功したとき「gr_check.php」からのレスポンスとして、実行するメールプログラムのパスを受け取ります。これをフォームのaction属性に設定し「document.gr_form.submit();」で送信を行います

gr_check.php

<?php $token = $_POST['response']; if($token) { $url = 'https://www.google.com/recaptcha/api/siteverify'; $secret_key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; $response = file_get_contents("{$url}?secret={$secret_key}&response={$token}"); $json = json_decode($response, true); if($json['success'] == 'true') { echo 'https://xxx.com/mail.cgi'; } } ?>
  • Googleへのリクエストを行うサーバーサイドの処理です
  • 「secret_key」にはGoogleから取得したシークレットキーを設定します
  • シークレットキーと、JavaScript側からの「XMLHttpRequest」で渡されてきたレスポンスキーを使い、「file_get_contents」でGoogleにリクエストを行います
  • リクエストの結果が成功であるなら、メールプログラムの実行パスをechoします。これがJavaScript側へのレスポンスとなります。

ご質問など受け付けています

記事の中でわかりにくかったところ、もっと知りたかったこと、間違っていることなど、何でもお気軽にご連絡ください。

ご連絡は下記フォームを利用いただくか、ツイッターアカウント@flat8migi宛てでもOKです。