Googleリキャプチャ(reCAPTCHA)v2/v3の導入 ~WordPressサイトにも対応~

この記事では「Googleリキャプチャ(reCAPTCHA)v2/v3の導入」についてどこよりもていねいに解説いたします。

全ての人が読めるようどんなWebサイトにも導入できるようパターン別に解説しています。
(WordPressのContact Form7のフォームへの導入についても解説あります。)

この記事を監修・執筆した専門家

こんにちは!この記事を監修・執筆した斎藤はじめと申します。
私は現役のエンジニアで、普段はWordPressの開発案件などをメインに担当しています。経歴としては誰もが知っている月間数千万人が利用するサービスの開発をしたり、今でも月間数百万人が訪問する規模のWordPressサイトの運営しております。WordPressを触らない日はありません。

そんな私がプロの目で解説していきます!

Googleリキャプチャ(reCAPTCHA)とは?

Googleリキャプチャ(reCAPTCHA)はGoogleが提供する「フォームの不正利用やスパム対策の認証システム」です。
データ送信者が人間なのかロボットかを判定し、ロボットによる自動送信をブロックすることができます。

そのロボットの判定の認証システム自体は一般的にCAPTCHA(キャプチャ)と言われています。
Google開発したキャプチャシステムが「Googleリキャプチャ(reCAPTCHA)」で、現在バージョン3までリリースされています。

この説明だけだとまだ「reCAPTCHAって何?」って思う方も多いと思いますが、おそらく次の項をお読みいただければ「ああ、あれか!見たことある!」ってなると思いますのでこのまま読んでみてください。

Google reCAPTCHAの各バージョンv1,v2,v3の違い

Googleリキャプチャ(reCAPTCHA)はv1、v2、v3とバージョンがありますが、それぞれどんな違いがあるのでしょうか?

バージョンごとに見ていきましょう!

v1 ゆがんだ文字画像を読み取るアレ(サービス終了)

Google reCAPTCHA v1のようなCAPTCHA認証システムはおそらく過去に1度くらいは見たことはあるでしょう。

歪んだ文字列の内容を読み取ってフォームに記入するタイプの認証システムです。

Google reCAPTCHA v1のサービス提供はすでに終了していますので、現在導入することはできません。

v2 お題の画像を選択が面倒で有名!?

Google reCAPTCHA v2は「チェックボックス+画像選択チャレンジ」のダブル認証という形式です。

まずはチェックボックスにチェックを入れることを求められます。

基本はユーザーがチェックボックスをクリックするだけですがその後、挙動や過去の履歴などから、あなたがロボットかどうか怪しければ画像選択チャレンジによる認証がはじまります。

見たことある人も多いかと思います。「オートバイの画像を全て選択してください」とかいうこれです。人間でもめちゃくちゃ迷うやつ笑

正直言ってこれめちゃくちゃ面倒ですよね?全然悪い事してないのにちょっとフォームのテストなど繰り返しただけで毎回出るようになることも多いです。

一度この画像選択チャレンジが出始めると毎回のように出るようになり、ロボットではなく人間が正常な動作をしている場合は、非常に煩わしく、ひどいケースでは一部の不正のせいでそのフォーム全体のコンバージョン率などが著しく低下してしまうこともあるようです。

設定でこの画像選択チャレンジの出る頻度を調整すること自体はできるのですが、一番最小にしても普通にブラウザからなんどかフォーム送信を繰り返すだけで、毎回出るようになるレベルなので全体的にちょっと出過ぎな印象です。

人間にとってわずらわしい分セキュリティが高いのですが、ユーザーの利便性などを考えると売り上げを左右するような申込フォームなどには使うのは避けた方がよいかもしれません。

v3 ユーザーは何もしなくてOK!自動で挙動をチェック

最新版であるv3は進化しました。

ユーザーは認証の際に何もしなくてよくなりました。
ブラウザの挙動やアクセス情報などから自動的に怪しさのスコアを算出してくれるのです。どの程度のスコアで拒否するのか判断するのは自分です。

v3を導入しているとサイトの下部に以下のようなバッジが表示されるのですが、非表示にしている場合が多いみたいなので、導入されていても気づかないパターンがほとんどです。

なので「v3を見たことある」人は少ないでしょう。ひっそりと導入されています。

v2とv3のメリット・デメリットを比較

v2とv3を比較してメリット・デメリットを見てみましょう。
次の2パターンで比較していきます。

  • WordPressのプラグイン「Contact Form7」で作られたフォームに導入する場合
  • ゼロから自力でWebサイトにreCAPTCHAを導入する場合

①WordPressのプラグイン「Contact Form7」で作られたフォームに導入する場合

Contact Form 7プラグインはreCAPTCHA v3に対応しているので簡単な設定で導入できます。v2は非対応です。

項目 Contact Form 7を使用した場合
(v2)
Contact Form 7を使用した場合
(v3)
ユーザーの利便性 〇ユーザーは何もしなくてよい
導入にかかる手間 〇20分ほどで設置可能
(基本管理画面操作のみ)
導入に利用するスキル 〇不要
(技術的なスキルはいらない)
サイトへの影響 〇ほぼなし

②ゼロから自力でWebサイトにreCAPTCHAを導入する場合

通常のWebサイトに自力で(Contact Form7のようにGoogle reCAPTCHA対応のフォームシステムを使わずに)導入する場合は結構大変です。

v2もv3もそれなりのスキルと手間がかかります。後程サンプルプログラムを紹介しますが、新しくフォームをゼロから作成するにはサーバーサイドプログラミングの知識が必須となります。

工数的にはv2とv3はほぼ同等とみてよいでしょう。

項目 自力でWebサイトに導入する場合
(v2)
自力でWebサイトに導入する場合
(v3)
コメント
ユーザーの利便性 ×画像の選択が出てしまうと非常に煩わしい 〇ユーザーは何もしなくてよい v3の圧勝
導入にかかる手間 △かなり大変(数日?)
(クライアント・サーバー両方に設置必須)
△かなり大変(数日?)
(クライアント・サーバー両方に設置必須)
設置にかかるコストはv2,v3ほぼ同等
導入に利用するスキル △ハードル高い
(HTML/JavaScript/PHP等)
△ハードル高い
(HTML/JavaScript/PHP等)
v2,v3ともに同じ技術が必要。
サーバー側言語はPHPでなくてもよいが、プログラムが書ける必要がある
サイトへの影響 ×不正などが多発した場合、画像選択チャレンジの起動率が上がりコンバージョン率が激減するなどのリスク 〇ほぼなし。自分で判断して調整できる。 ※v2の画像選択チャレンジの起動率は完全にコントロールできない
※v2の認証はOK/NG、v3の認証は0~1.0のスコアで示される

Google reCAPTCHAの必要性について

スパムメール自体の脅威に関してはこちらの記事で詳しく触れているのでよろしければご覧ください。

Googleリキャプチャ(reCAPTCHA)のよくある勘違い

Googleリキャプチャ(reCAPTCHA)でよくある勘違いをご紹介しておきます。

最初は筆者も勘違いしていたのですが、それはGoogleリキャプチャ(reCAPTCHA)のv2もv3も「JavaScriptを画面に貼り付ければ機能する、という誤解」です。

ちょっと調べてみたところ、そう勘違いしたままクライアント(JavaScript)側だけの処理で終えている記事も多く見受けられました。

確かにロボットの認証APIの実行だけはクライアント(ブラウザ)だけでも実行可能ですが、サーバー側と連携してプログラム処理を書かないと、認証の結果を使って判定していないので結局何もしてないか片手落ちの対応となってしまうのです。

例えるなら「裁判だけして、刑罰の執行はされない」みたいな。

また、スパム業者が直接プログラムから通信をしてきた場合、片手落ちの対応だとノーガードになってしまうのでひとたまりもありません。

Google reCAPTCHAはエンジニアでもゼロから理解して開発しはじめたら数日かかる案件です。Contact Form 7などの機能を使えば誰でも実装することができます。

エンタープライズ版との違い

Google reCAPTCHAは基本無料ですが、以下のような条件の元では「reCAPTCHA Enterprise」となり、有料になります。

こんなアクセス稼げるなら有料でも問題ないでしょ・・・ってくらい無料の幅が大きいので、ほとんどの人は無料で全く問題ありません。まずは無料で使ってみてください。

  • 1か月で100万以上のページで使う場合
  • 1秒間に1000リクエスト以上使う場合
  • サポートが欲しい
  • Webだけでなくアプリで使用したい
  • 他の追加セキュリティ機能も欲しい

※非営利目的での使用は申請すると許可が下りる場合があります。

Googleリキャプチャ(reCAPTCHA)v3の登録と設定

では最新版のv3の実装に進んでいきましょう!キーの登録が住んでいる方やv2を希望の方は読み飛ばしてください。

Googleリキャプチャ(reCAPTCHA)の公式から管理コンソールに進みます。

https://www.google.com/recaptcha/about/

ラベルは設定名なので何でもよいです。タイプにv3、ドメインを入力して送信。

サイトキー、シークレットキーの2つを後程使いますので控えておきましょう。(いつでもまた確認できます)

アナリティクスに移動。
管理画面のトップに移動します。まだ何のデータもないです。反映するまで2~3日かかります。

Googleリキャプチャ(reCAPTCHA)v3の実装と使い方

それでは実際の実装に進みます。次の3つの場合に分けて説明しますのでいずれかを読んでみてください。

  • 「WordPress」のログイン画面、コメント投稿欄、フォームなど色んなページでスパム&不正対策を実装したい
  • Contact Form7で作られたフォームに限ってv3実装する場合
  • 自力でWebサイトにreCAPTCHA v3を直接実装する場合

①「WordPress」のログイン画面、コメント投稿欄、フォームなど色んなページでスパム&不正対策を実装したい

WordPressをお使いならこちらが一番おすすめです。

こちらを読む対象はWordPressを使っていて、スパム&不正対策を行いたいページが「フォーム」も含め「ログイン画面」や「コメント投稿欄」など多岐にわたる場合です。

その場合は数あるreCAPTCHA導入プラグインの中でも「Invisible reCaptcha for WordPress」というプラグインが非常に便利です。これ一つインストールするだけでログイン画面や会員登録画面をはじめ、WooCommerceやContact Form 7など様々な他のプラグインで作ったページのスパム対策をすることができます。

「Invisible reCaptcha for WordPress」の導入に関しては、こちらの別記事に詳しく書いています。

②Contact Form7で作られたフォームにv3を実装する場合

Contact Form7で作られたフォームにだけスパム不正対策できればよいという方はContact Form 7本体が公式にreCAPTCHA対応しているのでその設定を使って導入しましょう。

この方法なら「Contact Form 7」以外に何かプラグインをインストールする必要もありません。

サイトキーとシークレットキーの登録をするだけ!

まずはサイト上にGoogleリキャプチャ(reCAPTCHA)v3のJavaScriptを読み込みます。

WordPressダッシュボード>お問い合わせ>インテグレーション>reCAPTCHA>セットアップ

先ほどの管理画面で控えたサイトキーとシークレットキーを入力して保存します。

これでGoogleリキャプチャ(reCAPTCHA)v3が読み込まれバッジが表示されるようになり、Contact Form7で作られたフォームなら何もしなくてもReCAPTCHAが利用できます。

めちゃくちゃ簡単ですね!!

Contact Form 7ではreCAPTCHAの認証スコア0~1.0のうち、0.5未満をロボットスパムとして扱います。

【おまけ】v3のロゴを消したり、位置をずらしたり、前面に移動したり

バッジが表示されたはいいものの、既存のパーツと位置がかぶってしまったり、そもそも表示させたくない!という場合もあると思います。

そんな時はCSSで.grecaptcha-badgeクラスを設定して調節してみてください。

【CSS追加場所の例】カスタマイズ>追加CSS

.grecaptcha-badge {

	/* 前面に出すなら値を大きく */
	z-index: 10000 !important;

	/* 表示を消す */
	visibility: hidden !important;

	/* 正の値を大きくすると上に、負なら下に移動 */
	bottom: 60px !important;

}

【おまけ】フォームを実装したページだけでreCAPTCHA v3を読み込みたい

Contact Form 7のデフォルトの設定では全ページにreCAPTCHAのJavaScriptを読み込みます。バッジを消せば済むけどもサイトのユーザービリティに影響あるかもしれないし、気持ち悪い・・・

フォームを実装したページだけでreCAPTCHA v3を読み込みたい場合はfunctions.php等に以下のコードを追記してみてください。
Contact Form 7のフォームが存在するページだけにreCAPTCHAが読み込まれるようになります。

<?php

//Contact Form 7で作られたフォームのページじゃなければreCAPTCHAを読み込みをキャンセル
//(デフォルトでContact Form 7にキー登録すると全ページreCAPTCHAが読み込まれてしまう)

add_action( 'wp_enqueue_scripts', function (){
	
	global $post;
	$valid_recaptcha = false;
	$content = get_post()->post_content;
	
	if($content != null){
		//Contact Form 7のショートコードが存在する
		if(has_shortcode($content, 'contact-form-7')) {
			$valid_recaptcha = true;	 
		}		
	}

	//ショートコードが存在しなければreCAPTCHAの読み込みをキャンセルする
	if($valid_recaptcha == false){
		wp_deregister_script( 'google-recaptcha' );
	}
	
}, 100);

Google reCAPTCHAを読み込むとGoogleのページ読込解析ツール(PageSpeed Insights)上のスコアがめちゃくちゃ下がる(20~30ダウン)なんていう記事も見かけましたが、筆者が試してみたところそんなことはありませんでした。

Google reCAPTCHAを読み込んでもPageSpeed Insightsのスコアは影響はなく、まったく下がらなかったです。

functions.phpの編集方法がわからない方はこちら。

functions.phpの編集方法

functions.phpはWordPressの機能追加をするためのPHPファイルです。

(※実ファイルは/wp-content/themes/<テーマ名>/の直下にあります)

functions.phpを修正する場合は必ず親テーマではなく子テーマのfunctons.phpを修正してください。

※その理由や親テーマと子テーマについてはこちら↓の記事を参考に。

管理画面からfunctions.phpを参照・編集するには以下のように「外観>テーマ(ファイル)エディター」にアクセスしてテキスト編集をします。

PHPファイルの記述ルールとしてプログラムコードは以下のようにphpタグの囲みの中にある必要があります。

<?php

	→PHPのプログラムを書く場所

?> ←最後PHPで終わる場合だけ省略可能

また、直接functions.phpを編集するよりもこちらのプラグインで編集した方が便利なので参考にしてみてください。

③自力でWebサイトにreCAPTCHA v3を直接実装する場合

こちらはWordPressを使わないサイトにゼロから自力でGoogle reCAPTCHAを直接導入する方法の解説です。

reCAPTCHA v3の動作の流れ

reCAPTCHA v3を自力で設置する場合は全体の流れがとってもややこしいので、理解しておくことが必須です。

  • ①ユーザーがフォームを開く
  • ②JavaScriptで設置したGoogle reCAPTCHA v3のライブラリが読み込まれる
  • ③ユーザーがフォームの送信ボタンを押す
  • ④いったんデータ送信をキャンセルし、以下の処理を行う
  • ⑤JavaScriptで認証APIが実行され、結果の取得に必要なトークンが発行される
  • ⑥フォームが送信される前にトークンをフォームデータの中に入れる
  • ⑦フォームデータ送信する
  • ⑧PHPなどのサーバープログラム内でトークンデータを受信
  • ⑨トークンを元にサーバープログラム内からAPIで認証結果を取得
  • ⑩0~1.0のスコア(基準としては0.5未満が怪しい)結果を元にフォームの処理をする

そのままアップロードすれば動くreCAPTCHA v3対応フォームのPHPサンプル

自力でWebサイトにGoogle reCAPTCHA v3を導入する方法の全てをイチから説明すると長くなってしまうので、PHPのサンプルプログラムをまずご紹介します。

サンプルフォームのイメージ

テキストを1つ入力して送信するだけ。
下の黒いテキストエリアはデバッグ用のエリアです。

フォーム送信してreCAPTCHAに成功するとこんな感じ。これだけのシンプルなフォームです。

送信ボタンを2度押ししたり、reCAPTCHA認証に失敗するとエラーになります。

このフォームが以下のソースコードをサーバーにアップロードしてキーを設定すればそれだけで動きます。

reCAPTCHA v3対応フォームのPHPサンプルソースコード
<?php
//##########################################################################
//Google reCAPTCHA v3を使ったテストフォーム
//##########################################################################

	//##########################################################################
	//初期設定
	//##########################################################################

	$site_key = "管理画面のサイトキーを入力"; //サイトキー
	$secret_key = "管理画面のシークレットキーを入力"; //シークレットキー

	ini_set( 'display_errors', 1 );// エラーを画面に出力する(本番実装時はオフ(0)にすること!)
	ini_set('error_reporting', E_ALL); //全てのエラーを出力
	//ini_set('error_reporting', E_COMPILE_ERROR | E_RECOVERABLE_ERROR | E_ERROR | E_CORE_ERROR); //致命的なエラーのみ表示

	//================================================================

	$debugs = array(); //debug用メッセージ格納
	$errors = array(); //表示用エラーメッセージ格納

	//##########################################################################
	//POSTデータ送信処理
	//##########################################################################
	if(isset($_POST) && count($_POST) > 0){

		$debugs["post"] = $_POST;
		if(isset($_POST["text-word"]) === true && strlen($_POST["text-word"]) > 12){ //テキストパラメータチェック
			$errors["text-word"] = "テキストは12文字までです";
		}
		//reCAPTCHA認証をユーザーが実行し、自分で設定したトークンデータが飛んできている
		if(isset($_POST["my-token"])){
			$ch = curl_init(); //cURLを初期化
			//curlオプションを設定
			curl_setopt($ch, CURLOPT_URL,"https://www.google.com/recaptcha/api/siteverify"); //URLを設定
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //curl_execの返り値を文字列にする
			curl_setopt($ch, CURLOPT_POST, true ); //POSTメソッド使用
			curl_setopt($ch, CURLOPT_POSTFIELDS, //POSTパラメータの指定
				http_build_query(array(
					'secret' => $secret_key,
					'response' => $_POST[ 'my-token' ]
				))
			);
			$response_json = curl_exec($ch); //curl実行
			curl_close($ch); //cURLを終了
			$response = json_decode( $response_json, true ); //レスポンスのjsonを配列にデコード

			$debugs["認証結果取得APIのレスポンス"] = $response;
			if (
					isset($response["success"])
					&& $response["success"] == 1
			) {
				$debugs["APIの判定"] = "ReCAPTCHAの認証成功";
				if (
					isset($response["score"])
					&& floatval($response["score"]) >= 0.6
				) {
					$debugs["recaptcha-score"] = "ReCAPTCHAの検証スコアに合格 {$response["score"]}";
				}else{
					$errors["recaptcha-score"] = "ReCAPTCHAの検証スコアに不合格 {$response["score"]}";
				}
			} else {
				$errors["recaptcha"] = "ReCAPTCHAの検証に失敗しました";
			}
		}else{
			$errors["recaptcha"] = "ReCAPTCHAのトークンがありません";
		}

    }

	$debugs["全エラー"] = $errors;
	//##########################################################################
	//①送信データの処理に全て成功した時のページ
	//##########################################################################
	if(isset($_POST) && count($_POST) > 0 && count($errors) == 0){

?>
<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>Google reCAPTCHA v3フォーム完了</title>
	<script>
	</script>
	<style>

        html{
            text-align: center;
        }

        body{
            padding: 1rem;
            margin: auto;
            width: 100%;
            max-width: 600px;
            box-sizing: border-box;
        }

        h1{
            margin: 0 auto;
            font-size: 1.2rem;
        }

        .my-debug{
            font-size: 0.8rem;
            white-space: pre-wrap;
            text-align: left;
            word-break: break-all;
            background-color: #eee;
            padding: 1rem;
            margin: 1rem auto;
            background-color: #000;
            color: #fff;
        }

        .my-message-info{
            padding: 0.5rem;
            border: 1px solid #99f;
            background-color: #ccf;
            color: #000;
            margin: 1rem auto;
        }

	</style>
</head>

<body>

	<h1>Google reCAPTCHA v3フォーム完了</h1>

	<div class="my-message-info">
		送信に成功しました!
	</div>

	<div style="text-align: center; padding: 1rem; margin: 2rem auto;">
		<a href="?">フォームに戻る</a>
	</div>

	<div class="my-debug"><b>デバッグ:</b><br/><?php echo htmlspecialchars(var_export($debugs, true)) ?></div>

</body>
</html>

<?php

	//##########################################################################
	//②送信データがないか、処理に失敗した時はフォームを表示
	//##########################################################################
    }else{

?>

<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>Google reCAPTCHA v3フォーム入力</title>
	<style>

        html{
            text-align: center;
        }

        body{
            padding: 1rem;
            margin: auto;
            width: 100%;
            max-width: 600px;
	        box-sizing: border-box;
        }

        h1{
            margin: 0 auto;
            font-size: 1.2rem;
        }

		#my-form{
			display: block;
			margin: 1rem auto;
			padding: 1rem;
			border: 1px solid #bbb;
			width: 100%;
            box-sizing: border-box;
		}

		#my-form input{
            display: block;
			margin: 1rem auto;
            width: 100%;
            box-sizing: border-box;
			padding: 0.5rem;
			font-size: 1.2rem;
		}

        #my-form input[type=submit]{
            background-color: #2196F3;
	        border: 0px solid #000;
	        color: #fff;
	        border-radius: 3px;
        }

        .my-message-error{
	        padding: 0.5rem;
	        border: 1px solid #f99;
	        background-color: #fcc;
	        color: #f00;
            margin: 1rem auto;
        }

        .my-debug{
            font-size: 0.8rem;
            white-space: pre-wrap;
            text-align: left;
            word-break: break-all;
            background-color: #eee;
            padding: 1rem;
	        margin: 1rem auto;
	        background-color: #000;
	        color: #fff;
        }

	</style>

</head>

<body>

	<h1>Google reCAPTCHA v3フォーム入力</h1>

<?php
		if(count($errors) > 0){
?>
	<!-- エラーメッセージ -->
	<div class="my-message-error">
<?php
			foreach($errors as $error_id => $error_message){
?>
				<div><?php echo $error_message ?></div>
<?php
			}
?>
	</div>
<?php
		}
?>
	<!-- 入力フォーム -->
	<form id="my-form" action="?" method="POST">

		<input type="text" name="text-word" value="<?php echo isset($_POST["text-word"]) ? htmlspecialchars($_POST["text-word"]) : ""; ?>" placeholder="何かテキストを入力">

		<input type="hidden" name="my-token" id="my-token">
		<input type="submit" value="送信する" onclick="">
	</form>
	<script src="https://www.google.com/recaptcha/api.js?render=<?php echo $site_key ?>"></script>
	<script>

		//reCAPTCHA認証APIを実行して返ってきたトークンをフォームに設置する関数
        function sendFormData(e) {
            e.preventDefault(); //元のsubmitをいったんキャンセル
            grecaptcha.ready(function() { //reCAPTCHAの準備ができてから実行
                //recaptcha実行 actionは任意の文字指定(管理画面で反映される)
                grecaptcha.execute('<?php echo $site_key ?>', {action: 'submit'})
	            .then(function(token) {
                    // Add your logic to submit to your backend server here.
	                console.log('grecaptcha.execute token=' + token);
                    document.getElementById('my-token').value = token; //recaptcha認証後のトークンをフォームで送信するために設定
                    console.log('フォームデータを送信');
                    document.getElementById("my-form").submit();
                })
                .catch(function(e){
                    console.error(e);
                    alert('エラーが発生したためフォームデータを送信できません');
                    return false;
                })
                ;
            });
        }

        //上で作成した関数をフォームデータ送信時に実行されるように設定
        document.getElementById("my-form").addEventListener('submit', sendFormData);

	</script>

	<div class="my-debug"><b>デバッグ:</b><br/><?php echo htmlspecialchars(var_export($debugs, true)) ?></div>

</body>
</html>

<?php

    }
	//##########################################################################

?>

ファイルのアップロードに困ったらこちらの記事のプラグインが超おすすめです。

v3実装のポイント解説

紹介したv3のソースコードをポイントに絞って解説していきます。

JavaScriptでGoogle reCAPTCHA v3ライブラリの呼び出し

以下のコードはライブラリの読み込みです。上記の全体の流れで示した②の部分です。

②JavaScriptで設置したGoogle reCAPTCHA v3のライブラリが読み込まれる

<script src="https://www.google.com/recaptcha/api.js?render=【コピーしたサイトキー】"></script>
reCAPTCHA認証を実行し、トークンをフォームに挿入してサーバー送信

続けて以下の部分が次のコードです。

③ユーザーがフォームの送信ボタンを押す
④いったんデータ送信をキャンセルし、以下の処理を行う
⑤JavaScriptで認証APIが実行され、結果の取得に必要なトークンが発行される
⑥フォームが送信される前にトークンをフォームデータの中に入れる
⑦フォームデータ送信する

	<script>

		//reCAPTCHA認証APIを実行して返ってきたトークンをフォームに設置する関数
        function sendFormData(e) {
            e.preventDefault(); //元のsubmitをいったんキャンセル
            grecaptcha.ready(function() { //reCAPTCHAの準備ができてから実行
                //recaptcha実行 actionは任意の文字指定(管理画面で反映される)
                grecaptcha.execute('<?php echo $site_key ?>', {action: 'submit'})
	            .then(function(token) {
                    // Add your logic to submit to your backend server here.
	                console.log('grecaptcha.execute token=' + token);
                    document.getElementById('my-token').value = token; //recaptcha認証後のトークンをフォームで送信するために設定
                    console.log('フォームデータを送信');
                    document.getElementById("my-form").submit();
                })
                .catch(function(e){
                    console.error(e);
                    alert('エラーが発生したためフォームデータを送信できません');
                    return false;
                })
                ;
            });
        }

        //上で作成した関数をフォームデータ送信時に実行されるように設定
        document.getElementById("my-form").addEventListener('submit', sendFormData);

	</script>
サーバー側で認証結果を参照

サーバーにトークンを送信したら、それを元にして認証の結果(スコア)をAPIで確認します。

			$ch = curl_init(); //cURLを初期化
			//curlオプションを設定
			curl_setopt($ch, CURLOPT_URL,"https://www.google.com/recaptcha/api/siteverify"); //URLを設定
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //curl_execの返り値を文字列にする
			curl_setopt($ch, CURLOPT_POST, true ); //POSTメソッド使用
			curl_setopt($ch, CURLOPT_POSTFIELDS, //POSTパラメータの指定
				http_build_query(array(
					'secret' => $secret_key,
					'response' => $_POST[ 'my-token' ]
				))
			);
			$response_json = curl_exec($ch); //curl実行
			curl_close($ch); //cURLを終了
			$response = json_decode( $response_json, true ); //レスポンスのjsonを配列にデコード

検証結果のスコアを元にフォーム自体の処理を自分で決める

APIでスコアが取得出来たら、0~1.0のスコアを元に判定処理を記述します。この例だと0.6より大きいと合格としています。一般的には0.5以上で合格、それ未満なら不合格とすることが多いと思います。Contact Form 7などは0.5以上で合格設定しているようです。

			$debugs["認証結果取得APIのレスポンス"] = $response;
			if (
					isset($response["success"])
					&& $response["success"] == 1
			) {
				$debugs["APIの判定"] = "ReCAPTCHAの認証成功";
				if (
					isset($response["score"])
					&& floatval($response["score"]) >= 0.6
				) {
					$debugs["recaptcha-score"] = "ReCAPTCHAの検証スコアに合格 {$response["score"]}";
				}else{
					$errors["recaptcha-score"] = "ReCAPTCHAの検証スコアに不合格 {$response["score"]}";
				}
			} else {
				$errors["recaptcha"] = "ReCAPTCHAの検証に失敗しました";
			}

v3の実装に関してはこれで終了です。
お疲れさまでした!

Googleリキャプチャ(reCAPTCHA)v2の登録と設定

v2の実装を進めていきます。

v2の導入をしない方も多いと思いますので、その場合は「Googleリキャプチャ(reCAPTCHA)を導入した結果」まで読み飛ばしてください。

Googleリキャプチャ(reCAPTCHA)の公式から管理コンソールに進みます。

https://www.google.com/recaptcha/about/

ラベルは設定名なので何でもよいです。
reCAPTCHAタイプにv2、「ロボットではありません」チェックボックスを選択、ドメインを入力して送信。

ちなみにv2の選択肢の「非表示reCAPTCHAバッジ」というのはチェックボックスなどユーザーの操作なしで判断して、怪しかったら画像認証チャレンジが始まるものです。

サイトキー、シークレットキーの2つを後程使いますので控えておきましょう。(いつでもまた確認できます)

アナリティクスに移動。
管理画面のトップに移動します。まだ何のデータもないです。反映するまで2~3日かかります。

Googleリキャプチャ(reCAPTCHA)v2の実装と使い方

ではv2の実装を進めましょう。

ちなみにWordPressの「Contact Form 7」はGoogleリキャプチャ(reCAPTCHA)v2には対応していません。

自分のWebサイトにreCAPTCHA v2を実装する

reCAPTCHA v2の動作の流れ

v2の大まかな流れを示しておきます。基本的にはv3と似ています。
実装前に必ず理解しておきましょう。

  • ①ユーザーがフォームを開く
  • ②JavaScriptで設置したGoogle reCAPTCHA v2のライブラリが読み込まれる
  • ③ユーザーがreCAPTCHAのチェックボックスをチェックする
  • ④怪しいと判断されたときのみ画像選択チャレンジが起動
  • ⑤認証OKならJavaScriptで認証APIが実行され、結果の取得に必要なトークンが発行される
  • ⑥トークンをフォームデータの中に入る(自動)
  • ⑦ユーザーがフォーム送信ボタンを押す
  • ⑧PHPなどのサーバープログラム内でトークンデータを受信
  • ⑨トークンを元にサーバープログラム内からAPIで認証結果を取得
  • ⑩OK/NGの結果を元にフォームの処理をする

そのままアップロードすれば動くreCAPTCHA v2対応フォームのPHPサンプル

自力でWebサイトにGoogle reCAPTCHA v2を導入する方法の全てをイチから説明すると長くなってしまうので、PHPのサンプルプログラムをまずご紹介します。

サンプルフォームのイメージ

テキストを1つ入力して、reCAPTCHAにチェックして送信するだけ。
下の黒いテキストエリアはデバッグ用のエリアです。

フォーム送信してreCAPTCHAに成功するとこんな感じ。これだけのシンプルなフォームです。

reCAPTCHAにチェックしないと送信できないようにしてあります。

送信ボタンを2度押ししたり、reCAPTCHA認証に失敗するとエラーになります。

これらのフォームが以下のソースをアップロードしてキーを設定するだけで動きます。

reCAPTCHA v2対応フォームのPHPサンプルソースコード
<?php
//##########################################################################
//Google reCAPTCHA v2を使ったテストフォーム
//##########################################################################

	//##########################################################################
	//初期設定
	//##########################################################################

	$site_key = "管理画面に表示されたサイトキー"; //サイトキー
	$secret_key = "管理画面に表示されたシークレットキー"; //シークレットキー

	ini_set( 'display_errors', 1 );// エラーを画面に出力する(本番実装時はオフ(0)にすること!)
	ini_set('error_reporting', E_ALL); //全てのエラーを出力
	//ini_set('error_reporting', E_COMPILE_ERROR | E_RECOVERABLE_ERROR | E_ERROR | E_CORE_ERROR); //致命的なエラーのみ表示

	//================================================================

	$debugs = array(); //debug用メッセージ格納
	$errors = array(); //表示用エラーメッセージ格納

	//##########################################################################
	//POSTデータ送信処理
	//##########################################################################
	if(isset($_POST) && count($_POST) > 0){

		$debugs["post"] = $_POST;
		if(isset($_POST["text-word"]) === true && strlen($_POST["text-word"]) > 12){ //テキストパラメータチェック
			$errors["text-word"] = "テキストは12文字までです";
		}
		//reCAPTCHA認証をユーザーが実行し、g-recaptcha-responseに自動設定されたトークンデータが飛んできている
		if(isset($_POST["g-recaptcha-response"])){
			$ch = curl_init(); //cURLを初期化
			//curlオプションを設定
			curl_setopt($ch, CURLOPT_URL,"https://www.google.com/recaptcha/api/siteverify"); //URLを設定
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //curl_execの返り値を文字列にする
			curl_setopt($ch, CURLOPT_POST, true ); //POSTメソッド使用
			curl_setopt($ch, CURLOPT_POSTFIELDS, //POSTパラメータの指定
				http_build_query(array(
					'secret' => $secret_key,
					'response' => $_POST[ 'g-recaptcha-response' ]
				))
			);
			$response_json = curl_exec($ch); //curl実行
			curl_close($ch); //cURLを終了
			$response = json_decode( $response_json, true ); //レスポンスのjsonを配列にデコード

			$debugs["認証結果取得APIのレスポンス"] = $response;
			if ( isset($response["success"]) && $response["success"] == 1) {
				$debugs["APIの判定"] = "ReCAPTCHAの認証成功";
			} else {
				$errors["recaptcha"] = "ReCAPTCHAの検証に失敗しました";
			}
		}else{
			$errors["recaptcha"] = "ReCAPTCHAのトークンがありません";
		}

    }

	$debugs["全エラー"] = $errors;
	//##########################################################################
	//①送信データの処理に全て成功した時のページ
	//##########################################################################
	if(isset($_POST) && count($_POST) > 0 && count($errors) == 0){

?>
<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>Google reCAPTCHA v2フォーム完了</title>
	<script>
	</script>
	<style>

        html{
            text-align: center;
        }

        body{
            padding: 1rem;
            margin: auto;
            width: 100%;
            max-width: 600px;
            box-sizing: border-box;
        }

        h1{
            margin: 0 auto;
            font-size: 1.2rem;
        }

        .my-debug{
            font-size: 0.8rem;
            white-space: pre-wrap;
            text-align: left;
            word-break: break-all;
            background-color: #eee;
            padding: 1rem;
            margin: 1rem auto;
            background-color: #000;
            color: #fff;
        }

        .message-info{
            padding: 0.5rem;
            border: 1px solid #99f;
            background-color: #ccf;
            color: #000;
            margin: 1rem auto;
        }

	</style>
</head>

<body>

	<h1>Google reCAPTCHA v2フォーム完了</h1>

	<div class="message-info">
		送信に成功しました!
	</div>

	<div style="text-align: center; padding: 1rem; margin: 2rem auto;">
		<a href="?">フォームに戻る</a>
	</div>

	<div class="my-debug"><b>デバッグ:</b><br/><?php echo htmlspecialchars(var_export($debugs, true)) ?></div>

</body>
</html>

<?php

	//##########################################################################
	//②送信データがないか、処理に失敗した時はフォームを表示
	//##########################################################################
    }else{

?>

<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>Google reCAPTCHA v2フォーム入力</title>
    <script src="https://www.google.com/recaptcha/api.js" async="" defer=""></script>
	<style>

        html{
            text-align: center;
        }

        body{
            padding: 1rem;
            margin: auto;
            width: 100%;
            max-width: 600px;
            box-sizing: border-box;
        }

        h1{
            margin: 0 auto;
            font-size: 1.2rem;
        }

        #my-form{
            display: block;
            margin: 1rem auto;
            padding: 1rem;
            border: 1px solid #bbb;
            width: 100%;
            box-sizing: border-box;
        }

        #my-form input{
            display: block;
            margin: 1rem auto;
            width: 100%;
            box-sizing: border-box;
            padding: 0.5rem;
            font-size: 1.2rem;
        }

        #my-form input[type=submit]{
            background-color: #2196F3;
            border: 0px solid #000;
            color: #fff;
            border-radius: 3px;
        }

        #my-message-error-grecaptcha{
            color: #f00;
            font-weight: bold;
        }

        .g-recaptcha>*{
            margin: auto;
        }

        .my-message-error{
            padding: 0.5rem;
            border: 1px solid #f99;
            background-color: #fcc;
            color: #f00;
            margin: 1rem auto;
        }

        .my-debug{
            font-size: 0.8rem;
            white-space: pre-wrap;
            text-align: left;
            word-break: break-all;
            background-color: #eee;
            padding: 1rem;
            margin: 1rem auto;
            background-color: #000;
            color: #fff;
        }

	</style>

</head>

<body>

	<h1>Google reCAPTCHA v2フォーム入力</h1>

<?php
		if(count($errors) > 0){
?>
	<!-- エラーメッセージ -->
	<div class="my-message-error">
<?php
			foreach($errors as $error_id => $error_message){
?>
				<div><?php echo $error_message ?></div>
<?php
			}
?>
	</div>
<?php
		}
?>
	<!-- 入力フォーム -->
	<form id="my-form" action="?" method="POST">

		<input type="text" name="text-word" value="<?php echo isset($_POST["text-word"]) ? htmlspecialchars($_POST["text-word"]) : ""; ?>" placeholder="何かテキストを入力">

		<div>送信する直前にチェックを入れてください</div>
		<div id="my-message-error-grecaptcha"><!-- CAPTCHAのエラーメッセージをここに表示 --></div>

		<!-- Google reCAPTCHAのウィジェットをここに表示 -->
		<div
			class="g-recaptcha"
			data-sitekey="<?php echo $site_key ?>"
			data-callback="verifyCallback"
			data-expired-callback="expiredCallback"
			data-error-callback="errorCallback"
		></div>

		<input type="submit" value="送信する" onclick="">
	</form>

	<script>
		//===========================================
		// フォーム内のreCAPTCHAのイベント設定
        //===========================================
		var is_success_recaptcha = false; //recaptchaのステータス。trueにならないと送信できない

		//reCAPTCHAに成功した時に実行されるコールバック関数
		var verifyCallback = function(response) {
			is_success_recaptcha = true;
			console.log('reCAPTCHA V2 成功');
			document.getElementById("my-message-error-grecaptcha").textContent = ''; //エラーメッセージを消す
		};

		//チェック後、2分間立って期限切れを起こした時に実行されるコールバック関数
		var expiredCallback= function(response) {
			is_success_recaptcha = false;
			console.warn('reCAPTCHA V2 期限切れ');
			document.getElementById("my-message-error-grecaptcha").textContent = '有効期限が切れました。再度チェックしてください';
		};

		//reCAPTCHAにネットワーク接続エラー等のエラーが起きた時に実行されるコールバック関数
		var errorCallback= function(response) {
			is_success_recaptcha = false;
			console.error('reCAPTCHA V2 エラー');
			document.getElementById("my-message-error-grecaptcha").textContent = 'reCAPTCHAエラーが発生しました。ネットワーク接続などを確認してページを再読み込みしてください。';
		};

		//フォームデータ送信前何かするための関数(必須ではない。トークンはフォームに自動設置される)
		function sendFormData(e) {
			console.log("sendFormData実行");
			e.preventDefault(); //元のsubmitをいったんキャンセル
			if(is_success_recaptcha){
				console.log('ステータス有効。フォームデータを送信');
				document.getElementById("my-form").submit();
			}else{
				console.error('ステータスチェックで送信失敗');
				alert('送信する前に「ロボットではありません」にチェックを入れてください');
				return false;
			}
		}

        //上で作成した関数をフォームデータ送信時に実行されるように設定
        document.getElementById("my-form").addEventListener('submit', sendFormData); //送信ボタンがクリックされたときに起動する関数を設定

	</script>

	<div class="my-debug"><b>デバッグ:</b><br/><?php echo htmlspecialchars(var_export($debugs, true)) ?></div>

</body>
</html>

<?php

    }
	//##########################################################################

?>

ファイルのアップロードに困ったらこちらの記事のプラグインが超おすすめです。

v2実装のポイント解説

紹介したv2のソースコードをポイントに絞って解説していきます。

JavaScriptでGoogle reCAPTCHA v2ライブラリの呼び出し

以下のコードはライブラリの読み込みです。上記の全体の流れで示した②の部分です。

②JavaScriptで設置したGoogle reCAPTCHA v2のライブラリが読み込まれる

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

チェックボックスの表示

フォームの中にチェックボックスを表示させたい場所に以下のhtmlを埋め込みます。これによってreCAPTCHA認証を実行し、トークンをフォームに自動挿入できるようになります。

		<!-- Google reCAPTCHAのウィジェットをここに表示 -->
		<div
			class="g-recaptcha"
			data-sitekey="<?php echo $site_key ?>"
			data-callback="verifyCallback"
			data-expired-callback="expiredCallback"
			data-error-callback="errorCallback"
		></div>

これを埋め込めばトークンの設定などは自動で行われPOSTデータとして飛びます。

上記の全体の流れの部分で言うとこれらに当たります。

③ユーザーがreCAPTCHAのチェックボックスをチェックする
④怪しいと判断されたときのみ画像選択チャレンジが起動
⑤認証OKならJavaScriptで認証APIが実行され、結果の取得に必要なトークンが発行される
⑥トークンをフォームデータの中に入る(自動)

ウィジェットのタグの属性オプション
属性 説明
data-sitekey サイトキー
data-theme ウィジェットのテーマ(light/dark)※デフォルトlight
data-callback 認証に成功した時に呼び出されるコールバック関数
data-expired-callback チェックした後期限切れ(2分間)すると呼び出されるコールバック関数
data-error-callback エラー(ネットワーク接続のエラー等)が発生した場合に呼び出されるコールバック関数
コールバック関数を設定する

ユーザーが送信を押した後、認証が成功した時、失敗した時の処理などを書いていきます。

全体の流れではこの部分

⑦ユーザーがフォーム送信ボタンを押す

属性に指定したコールバックを一つずつ作成します。


	<script>
		//===========================================
		// フォーム内のreCAPTCHAのイベント設定
        //===========================================
		var is_success_recaptcha = false; //recaptchaのステータス。trueにならないと送信できない

		//reCAPTCHAに成功した時に実行されるコールバック関数
		var verifyCallback = function(response) {
			is_success_recaptcha = true;
			console.log('reCAPTCHA V2 成功');
			document.getElementById("my-message-error-grecaptcha").textContent = ''; //エラーメッセージを消す
		};

		//チェック後、2分間立って期限切れを起こした時に実行されるコールバック関数
		var expiredCallback= function(response) {
			is_success_recaptcha = false;
			console.warn('reCAPTCHA V2 期限切れ');
			document.getElementById("my-message-error-grecaptcha").textContent = '有効期限が切れました。再度チェックしてください';
		};

		//reCAPTCHAにネットワーク接続エラー等のエラーが起きた時に実行されるコールバック関数
		var errorCallback= function(response) {
			is_success_recaptcha = false;
			console.error('reCAPTCHA V2 エラー');
			document.getElementById("my-message-error-grecaptcha").textContent = 'reCAPTCHAエラーが発生しました。ネットワーク接続などを確認してページを再読み込みしてください。';
		};

		//フォームデータ送信前何かするための関数(必須ではない。トークンはフォームに自動設置される)
		function sendFormData(e) {
			console.log("sendFormData実行");
			e.preventDefault(); //元のsubmitをいったんキャンセル
			if(is_success_recaptcha){
				console.log('ステータス有効。フォームデータを送信');
				document.getElementById("my-form").submit();
			}else{
				console.error('ステータスチェックで送信失敗');
				alert('送信する前に「ロボットではありません」にチェックを入れてください');
				return false;
			}
		}

        //上で作成した関数をフォームデータ送信時に実行されるように設定
        document.getElementById("my-form").addEventListener('submit', sendFormData); //送信ボタンがクリックされたときに起動する関数を設定

	</script>
サーバー側で認証結果を参照

サーバーにトークンを送信したら、それを元にして認証の結果(成功/失敗)をAPIで確認します。

			$ch = curl_init(); //cURLを初期化
			//curlオプションを設定
			curl_setopt($ch, CURLOPT_URL,"https://www.google.com/recaptcha/api/siteverify"); //URLを設定
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //curl_execの返り値を文字列にする
			curl_setopt($ch, CURLOPT_POST, true ); //POSTメソッド使用
			curl_setopt($ch, CURLOPT_POSTFIELDS, //POSTパラメータの指定
				http_build_query(array(
					'secret' => $secret_key,
					'response' => $_POST[ 'g-recaptcha-response' ]
				))
			);
			$response_json = curl_exec($ch); //curl実行
			curl_close($ch); //cURLを終了
			$response = json_decode( $response_json, true ); //レスポンスのjsonを配列にデコード

	
検証結果を元にフォーム自体の処理を自分で決める

APIの結果を元に自分で判定処理を行います。
v3とは違いv2の場合は成功/失敗の2パターンが結果で返ります。

			if ( isset($response["success"]) && $response["success"] == 1) {
				$debugs["APIの判定"] = "ReCAPTCHAの認証成功";
			} else {
				$errors["recaptcha"] = "ReCAPTCHAの検証に失敗しました";
			}

Googleリキャプチャ(reCAPTCHA)を導入した結果

Googleリキャプチャ(reCAPTCHA)を導入した結果どうなったかを報告しておきます

結果としてはスパムメール大激減!スパム対策としての効果バッチリ!でした。
惜しくもコメント欄から防ぎきれないスパムがたまにあった程度です。

サイト 導入前スパム/月 導入後スパム/月
サイトA 30~300件(フォーム) 0件
サイトB 150~200件(コメント) 2件

また、管理画面ではこのように表示されます。
(上部の2グラフは数日、下部はトラフィックが数百ほどないと表示されないみたいです)
「homepage」というaction名は「Contact Form 7」が指定しているものです。

と、言うわけで自信をもって導入をおすすめいたします!

まとめ

いかがでしたでしょうか?

かなり大変だったとは思いますが、Google reCAPTCHAはスパム対策としてはこの上ないくらい効果的な対策だと思いますので、何度か読み返してでもぜひ実装してみてくださいね!

最後に関する記事を紹介しておきます。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です