reCAPTCHA v3の認証エラー(timeout-or-duplicate)対応

reCAPTCHA v3 で、エラーにならなくてもいいのに発生していたエラー( timeout-or-duplicate )に対応したメモです。

    追記:本記事の対応は真似しない方がよい

    ご指摘いただいたので追記。

    エラーを無視するのではなく、他の方法で回避すべきとのこと。仰る通りなので、本記事の対応は真似しない方がよい。

    しかしこのエラーコードは名前の通り、トークン重複利用の場合でも返ってくる。要は一回人間が操作したときのreCAPTCHAのフォーム値が再利用できるようになるということで、いくらでもbotが通過できるようになり、reCAPTCHAが無意味になる。

    (略)

    このエラーの発生を防ぐためには、reCAPTCHA v3についてはユーザアクション時(例えばフォーム送信時)にトークン発行することをドキュメントでは勧めている。

    reCAPTCHAのtimeout-or-duplicateは無視してはいけない – 仮メモ

    Note: reCAPTCHA tokens expire after two minutes. If you’re protecting an action with reCAPTCHA, make sure to call execute when the user takes the action rather than on page load.

    reCAPTCHA v3  |  Google Developers

    恥ずかしい記事を公開してしまったので、時間が出来たらちゃんとした対応を書いてアップしたい。以下の記事では、後半で具体的な実装を紹介されているので参考にされたい。

    reCAPTCHA v3 とは

    reCAPTCHAはbotを判別してフォームをスパムから守る仕組みです。

    最新バージョンの reCAPTCHA v3 ではユーザーの行動から算出したスコアでbotを識別しており、いままでのように「私はロボットではありません」にチェックを入れたり、読みにくい文字を読んだり画像をしたり…というユーザーの手間がなくなりました。

    エラーの種類

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

    timeout-or-duplicate エラー

    いくつかあるエラーのうち、特にクセモノなのがtimeout-or-duplicate(タイムアウトまたは既に使われた値)のエラーです。

    特にタイムアウトは2分に設定されているらしく、フォーム画面を開いてトークンを取得してからページを開いたまま2分経つと認証エラーになる模様です。入力する分量が多かったり、なにか資料などを確認する必要があったりすると割と簡単に経過しそうな時間なので、特に不正ではなくとも普通にエラーが発生しそうです(しました)。また、設定時間の変更はできないようです。

    timeout-or-duplicate エラーを処理する

    今回はtimeout-or-duplicate エラーが発生しても通常通りの処理をするように対応しました。

    エラー内容の取得

    PHP
    $response = file_get_contents('https://www.google.com/recaptcha/api/siteverify?secret=' . $recaptcha_secret_key . '&response=' . $recaptcha_token);
    $verify = json_decode($response);
    
    if($verify->success) {
    	// 成功
    } else {
    	// 失敗
    	$error = $verify->{'error-codes'};
    }

    注意点

    上記はPHPでの処理例ですが、レスポンスのerror-codesがハイフンを含むため、{}で囲う必要があります。

    PHP
    // エラー
    $error = $verify->error-codes;
    
    // OK
    $error = $verify->{'error-codes'};

    あとどうでもいいけどテストする時にいちいち待つのが地味にめんどくさいですね。

    レスポンスの中身

    $verifyをログに出すと以下のようなレスポンスが返ってきていました。successの値はboolが入るけどなんか出力時に1と空になってる。

    成功時

    stdClass Object
    (
    	[success] => 1
    	[challenge_ts] => 2020-07-13T03:36:27Z
    	[hostname] => example.com
    	[score] => 0.9
    	[action] => submit
    )

    エラー時

    stdClass Object
    (
    	[success] => 
    	[error-codes] => Array
    		(
    			[0] => timeout-or-duplicate
    		)
    )

    エラー時の判定

    複数のエラーが発生していた場合(発生しうるのか?)はちゃんとエラー扱いにしたいので、ここではエラーコードの個数もカウントしています。

    PHP
    if( count($error) == 1 && $error[0] == 'timeout-or-duplicate' ){
    	//timeout-or-duplicateエラーを無視する処理
    }else{
    	// 失敗処理
    }

    参考

    サポートが必要ですか?

    ご質問・お見積り依頼はお気軽にどうぞ

    お問い合わせはこちら
    シェア
    野良人 代表
    新免祥太
    1988年岡山生まれ。外食企業のWEB・EC担当を経験したのち、2013年12月より「野良人(のらんど)」の屋号で独立しWEBデザイン・プログラミングなどWEBサイト制作の工程全般を請け負っています。お気軽にご相談ください。
    広告
    次の記事(2020/12/31)
    【Stripe】ダッシュボードで作成した商品のCheckout決済を実装する
    前の記事(2020/12/05)
    Local(local by flywheel)がDockerのTLS証明書エラーで起動できずにループする現象に対応
    記事一覧