サンプル・プログラムの実行例
reCAPTCHAは、この画面で「送信」ボタンをクリックするのがボットではなく、人間であることを判定する。
目次
サンプル・プログラムのダウンロード
| message.php | サンプル・プログラム |
| .pahooEnv | クラウドサービスを利用するためのアカウント情報などを記入する .env ファイル。 使い方は「各種クラウド連携サービス(WebAPI)の登録方法」を参照。include_path が通ったディレクトリに配置すること。 |
| pahooInputData.php | データ入力に関わる関数群。 使い方は「数値入力とバリデーション」「文字入力とバリデーション」などを参照。include_path が通ったディレクトリに配置すること。 |
| バージョン | 更新日 | 内容 |
|---|---|---|
| 2.1.0 | 2025/09/15 | .pahooEnv導入 |
| 2.0 | 2020/05/29 | reCAPTCHA v3導入 |
| 1.1 | 2009/10/28 | 受付番号を付加 |
| 1.0 | 2009/08/25 | 初版 |
| バージョン | 更新日 | 内容 |
|---|---|---|
| 2.0.1 | 2025/08/11 | getParam() bug-fix |
| 2.0.0 | 2025/08/11 | pahooLoadEnv() 追加 |
| 1.9.0 | 2025/07/26 | getParam() 引数に$trim追加 |
| 1.8.1 | 2025/03/15 | validRegexPattern() debug |
| 1.8.0 | 2024/11/12 | validRegexPattern() 追加 |
準備:PHP の https対応
Windowsでは、"php.ini" の下記の行を有効化する。
extension=php_openssl.dllLinuxでは --with-openssl=/usr オプションを付けて再ビルドする。→OpenSSLインストール手順
これで準備は完了だ。
準備:pahooInputData 関数群
また、各種クラウドサービスに登録したときに取得するアカウント情報、アプリケーションパスワードなどを登録した .pahooEnv ファイルから読み込む関数 pahooLoadEnv を備えている。こちらについては、「各種クラウド連携サービス(WebAPI)の登録方法」をご覧いただきたい。
準備:reCAPTCHAの利用登録
reCAPTCHA にはV2とV3があるが、今回は利用者に負担をかけることが少ないV3を使用することにする。
また、Googleアカウントがない方は、事前にアカウントを入手してほしい。
Google reCAPTCHAの右上にある「Admin console」をクリックする。初回は左図のような画面が表示されるので、必要事項を選択し、reCAPTCHA を使用するドメインを入力し、「送信」を押下する。
サイトキー と シークレットキー は、これから作るプログラムで利用するので、メモしておく。
message.php
55: // 各種定数(START) ===========================================================
56:
57: // チェックするURLパターン
58: define('PAT_URL', "/^https?\:\/\/[a-z\.]*pahoo\.org/ui");
59:
60: // 送信先メールアドレス
61: define('MAIL_TO', 'aaaa@bbbb.ccc');
62:
63: // Google reCAPTCHA v3
64: // https://www.google.com/recaptcha/intro/v3.html から無償入手
65: // サイトキー
66: if (isset($_ENV['PAHOO_RECAPTCHA_SITE_KEY'])) {
67: define('SITE_KEY', $_ENV['PAHOO_RECAPTCHA_SITE_KEY']);
68: } else {
69: define('SITE_KEY', '');
70: }
71: // シークレットキー
72: if (isset($_ENV['PAHOO_RECAPTCHA_SECRET_KEY'])) {
73: define('SECRET_KEY', $_ENV['PAHOO_RECAPTCHA_SECRET_KEY']);
74: } else {
75: define('SECRET_KEY', '');
76: }
77:
78: // トークンネーム(固定)
79: define('TOKEN_NAME', 'recaptchaToken');
80:
81: // 表示幅(ピクセル)
82: define('WIDTH', 600);
83:
84: // 各種定数(END) ===============================================================
今回のプログラムでは、問い合わせ内容をサイト管理者のメールアドレスへ送信する。送信先メールアドレスは定数 MAIL_TO に、問い合わせURLが自サイトかどうかを判別するための正規表現パターンを定数 PAT_URL に、それぞれ定義しておく。
reCAPTCHAの仕組み
取得したトークンと、その他入力データをセットにしてスコア判定プログラム(サーバサイド)に送る。判定プログラムは入手したトークンと、シークレットキーを使い、Google reCAPTCHA サーバにトークン検査を要求する。すると、そのトークンに対応するスコアが返される。スコアの価は0.0以上1.0以下で、おおむね0.5未満はbotの可能性がある。
Google reCAPTCHA サーバがどのような方法でスコア計算しているかは明らかにされていないが、当サイトでは、この reCAPTCHA を導入することで、迷惑メッセージが皆無になったことから、結果として効果を発揮している。
プログラムの流れ
入力チェック画面
message.php
276: /**
277: * HTML BODYを作成する - 入力チェック用
278: * @param string $name ニックネーム
279: * @param string $url 関連URL
280: * @param string $message メッセージ
281: * @param bool $private 非公開フラグ
282: * @return string HTML BODY
283: */
284: function makeBodyCheck($name, $url, $message, $private) {
285: $myself = MYSELF;
286:
287: // 入力チェック
288: $flag = TRUE;
289: if ($name == '') {
290: $flag = FALSE;
291: $name_msg = '<span class="red">※ニックネームは必須です.入力してください.</span>';
292: } else {
293: $name_msg = '';
294: }
295:
296:
297: if (($title = checkURL($url)) == FALSE) {
298: $flag = FALSE;
299: $url_msg = '<span class="red">※当サイトのURLではありません.再入力してください.</span>';
300: } else {
301: $url_msg = '';
302: }
303:
304: if ($message == '') {
305: $flag = FALSE;
306: $message_msg = '<span class="red">※メッセージは必須です.入力してください.</span>';
307: } else {
308: $message_msg = '';
309: }
310:
311: // 非公開フラグの処理
312: $private_msg = $private ? 'メッセージは非公開にする.' : 'メッセージは公開する.';
313: $private = $private ? '<input type="hidden" name="private" value="TRUE">' : '';
314:
315: // ボタン作成
316: $button = $flag ? '<input type="submit" name="send" value="送信">' : '';
317:
318: // reCAPTCHA v3対応フォーム
319: $site_key = SITE_KEY;
320: $token_name = TOKEN_NAME;
321: $body =<<< EOT
322: <script src="https://www.google.com/recaptcha/api.js?render={$site_key}" async defer></script>
323: <script>
324: grecaptcha.ready(function() {
325: grecaptcha.execute('{$site_key}',
326: {action:'contact'}).then(function(token) {
327: var token_name = document.getElementById('{$token_name}');
328: token_name.value = token;
329: });
330: });
331: </script>
332: <!-- タイトル -->
333: <h1 class="index1">メッセージ内容確認</h1>
334:
335: <!-- 本文 -->
336: <div class="main">
337: <p>
338: ご意見、ご質問、励ましのメッセージを当サイト管理者へ送信します。<br>
339: 下記の内容で問題なければ、「送信」ボタンをクリックしてください。
340: </p>
341:
342: <p>
343: ●ニックネーム {$name_msg}
344: <blockquote class="quote2">{$name}</blockquote>
345: </p>
346: <p>
347: ●関連URL {$url_msg}<span class="blue">{$title}</span>
348: <blockquote class="quote2">{$url}</blockquote>
349: </p>
350: <p>
351: ●メッセージ {$message_msg}
352: <blockquote class="quote2">{$message}</blockquote>
353: ●{$private_msg}
354: </p>
355: <form name="form_message" id="form_message" method="post" action="{$myself}">
356: <div>
357: <input type="submit" name="input" id="input" value="入力画面に戻る">
358: {$button}
359: </div>
360: <input type="hidden" name="name" id="name" value="{$name}">
361: <input type="hidden" name="url" id="url" value="{$url}">
362: <input type="hidden" name="message" id="message" value="{$message}">
363: <input type="hidden" name="{$token_name}" id="{$token_name}">
364: {$private}
365: </form>
366:
367: <p>
368: <a href="/">トップページへ戻る</a>
369: </p>
370: </div>
371: </body>
372:
373: EOT;
374: return $body;
375: }
前半で、入力データの検査を行う。
中盤(表示HTMLのbodyタグの冒頭)に、reCAPTCHA にトークン要求をするJavaScriptを記述する。
取得したトークンは定数 TOKEN_NAME で定義されたオブジェクトに格納され、次の画面に渡される。
reCAPTCHA v3 APIからデータ取得
message.php
194: /**
195: * reCAPTCHA v3 APIからデータ取得
196: * @param string $token トークン
197: * @return array 応答データ
198: */
199: function get_reCAPTCHA($token) {
200: $secret_key = SECRET_KEY;
201:
202: $ch = curl_init();
203: curl_setopt($ch, CURLOPT_URL, 'https://www.google.com/recaptcha/api/siteverify');
204: curl_setopt($ch, CURLOPT_POST, TRUE);
205:
206: // API パラメータの指定
207: curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array(
208: 'secret' => $secret_key,
209: 'response' => $token
210: )));
211: curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
212:
213: $api_response = curl_exec($ch);
214: curl_close($ch);
215:
216: return json_decode($api_response);
217: }
Google reCAPTCHA は、入力パラメータ(IN)は POST 渡しで、出力結果(OUT)は JSON で戻るというWebAPIである。
| URL |
|---|
| https://www.google.com/recaptcha/api/siteverify |
| フィールド名 | 要否 | 内 容 |
|---|---|---|
| secret | 必須 | シークレットキー |
| response | 必須 | トークン |
送信完了
message.php
479: // 処理分岐
480: if (isset($_POST['check'])) {
481: $HtmlBody = makeBodyCheck($name, $url, $message, $private);
482: } else if (isset($_POST['send'])) {
483: $res = get_reCAPTCHA($token);
484: if (isset($res->success) && $res->success && isset($res->score) && ($res->score >= 0.5)) {
485: $HtmlBody = makeBodySend($name, $url, $message, $private);
486: } else {
487: $HtmlBody = makeBodyFail($name, $url, $message, $private);
488: }
489: } else {
490: $HtmlBody = makeBodyInput($name, $url, $message, $private);
491: }
492:
493: // 表示処理
494: echo $HtmlHeader;
495: echo $HtmlBody;
496: echo $HtmlFooter;
そうでなければ、ユーザー関数 makeBodyFail を実行し、メール送信せずに、送信失敗したことをユーザーに通知する。
前半で、入力データの検査を行う。
偽reCAPTCHA
実際、2024年(令和6年)12月に旅行予約サイト「Booking.com」を装うフィッシング詐欺に利用され、この攻撃は2025年(令和7年)2月時点でも続いている。ユーザーが画面の指示に従うとパスワードなどの情報を盗み出すマルウェアに感染し、アカウントを乗っ取られたり決済情報を盗まれたりする恐れがある。
ClickFix と呼ばれる手口では、普段利用しているサービスのログイン画面に見せかけた詐欺サイトにユーザーを誘導し、「私はロボットではありません」を装う偽の reCAPTCHA ボタンを表示する。ユーザーがこのボタンをクリックすると、確認のためと称して次のようなキーボード操作を求められる。
- Windowsボタンと「R」を押す
- 「CTRL」と「V」を押す
- 「Enter」を押す
利用者側としては、 reCAPTCHA はブラウザ以外の操作を求めることはないことを認識しておきたい。
提供者側としては、自サイトが則られたり改ざんされることがないよう、定期的にサイト点検しておきたい。
生成AIによる突破
このようなAIエージェントを正確に検出するために、人間がコンピューターと物理的にやり取りする際の独特の行動パターンや認知特性に焦点を当てた新しいアプローチに注目が集まっている。
たとえば Proof-of-Human API は、このような行動パターンや認知特性をベースに人間とAIを区別することができるという。
参考サイト
- Google reCAPTCHA
- Google reCAPTCHA の使い方(v2/v3):Web Design Leaves
- GoogleのreCAPTCHAはどうやって人間とボットを見分けているのか?:GIGAZINE
- 偽のGoogle reCAPTCHAやMSログインページでアカウント窃取、企業幹部は特に注意を:ITmedia, 2021年03月10日
- その「私はロボットではありません」は本物? マルウェア感染狙う“偽CAPTCHA”出現 米Microsoftが注意喚起:ITmedia, 2025年3月26日
- 「CAPTCHAを突破してしまうAI」を正確に検出するために必要なアプローチとは?:GIGAZINE, 2025年6月27日
- 「私はロボットではありません」の偽画面に注意:ASCII.jp+McAfee, 2025年10月31日

みなさんも、サイトの入力画面に左図のロゴが表示されるのをご覧になったことがあるだろう。当サイトでも、「メッセージ受付」画面で reCAPTCHA を利用している。
今回は、PHPを使い、このメッセージ受付画面と同じものを作ってみることにする。