LaravelにreCAPTCHA (v3) を導入する
はじめに
この記事では「えんさがそっ♪」の LaravelアプリケーションにreCAPTCHA( v3 )を導入する方法についてまとめています。
v2とv3の違いについて
reCAPTCHAには v2 と v3 の 2 種類存在し、認証方法が異なります。
Ver | 内容 |
---|---|
v2 | 人間かbotか怪しいリクエストがきた時に、画像やチェックボックスによる認証をさせて、botをシャットダウンしようとします。 |
v3 | 認証機能がなく、全てのリクエストを通してスコア判定します。bot判定されたユーザーを拒否するには追加の設定が必要になります。 |
今回は v3 を採用しており、フォームの不正送信を防ぐためにreCAPTCHA側が設定したスコア判定によって、エラー表示をさせるところまでを説明していきます。
環境
- Laravel v9.52
- arcanedev/no-captcha v13.0
導入手順
Google reCAPTCHAの登録
reCAPTCHAの Admin Console から登録してください。
登録後にシークレットキーとサイトキーが発行されます。後述の環境変数の設定で使用するので、控えておいてください。
「arcanedev/no-captcha」のライブラリをインストール
自前で構築することもできますが、「環境構築が比較的簡単」、「他サイトでの使用実績も多い」ということで arcanedev/no-captcha を採用しました。
composer require arcanedev/no-captcha:"^13.0"
環境変数(env)設定
先ほど発行したシークレットキーとサイトキーを記入します。
NOCAPTCHA_SECRET=XXXXXXXXXXXXXXX
NOCAPTCHA_SITEKEY=XXXXXXXXXXXXXXX
configファイルを作成
/config/no-captcha.php
にconfigファイルを作成します。
<?php
return [
/* -----------------------------------------------------------------
| Credentials
| -----------------------------------------------------------------
*/
'secret' => env('NOCAPTCHA_SECRET', 'no-captcha-secret'),
'sitekey' => env('NOCAPTCHA_SITEKEY', 'no-captcha-sitekey'),
/* -----------------------------------------------------------------
| Version
| -----------------------------------------------------------------
| Supported: v3, v2
*/
'version' => 'v3',
/* -----------------------------------------------------------------
| Localization
| -----------------------------------------------------------------
*/
'lang' => 'jp',
/* -----------------------------------------------------------------
| Skip IPs
| -----------------------------------------------------------------
*/
'skip-ips' => [
// 127.0.0.1
],
];
フロントエンドの実装
formタグの中に{!! no_captcha()->input() !!}
を追加します。
<form action="/complete" method="post" id="form">
{{ csrf_field() }}
<dl>
<dt>名前</dt>
<dd><input type="text" name="name"></dd>
<dt>お問い合わせ内容</dt>
<dd><textarea name="content" /></dd>
</dl>
//reCAPTCHAのエラーが発生した時のエラーメッセージ
//エラーの種類問わず、共通のメッセージを表示しています
@if(session('isRecaptchaError'))
<span class="error__message">メッセージの送信に失敗しました。</span>
@endif
<button type="submit" id="submit_button">送信する</button>
{!! no_captcha()->input() !!}
</form>
フッターにreCAPTCHAのスクリプトを追加します。
no_captcha()->script()
とno_captcha()->getApiScript()
はscriptタグが含まれているのでエスケープ処理を入れてます。
{!! no_captcha()->script() !!}
{!! no_captcha()->getApiScript() !!}
<script>
const btn = document.getElementById('submit_button');
btn.addEventListener('click', function (e) {
e.preventDefault();
grecaptcha.ready(function () {
const siteKey = {{ Js::from(config('no-captcha.sitekey')) }}
grecaptcha.execute(siteKey, {action: 'Submit'}).then(function (token) {
document.getElementById('g-recaptcha-response').value = token;
document.getElementById('form').submit();
})
})
}, false);
</script>
エラー処理の設定
reCAPTCHAによるエラーが発生した時は、Controllerにリダイレクト処理を追加します。
public function complete(Request $request): View|RedirectResponse
{
if ($this->hasReCaptchaError($request)) {
return redirect()->back()->withInput($request->all())->with('isRecaptchaError', true);
}
--- 以下省略 ---
return view('complete');
}
同Controllerにエラー判定のメソッドhasReCaptchaError
を追加します。
isSuccess
:トークン認証やreCAPTCHAへのアクセスに失敗した時はFalse、それ以外はTrue
getScore
:reCAPTCHAが設定したスコアが入ります(閾値範囲 0.0 ~ 1.0)
private function hasReCaptchaError(Request $request): bool
{
/** @var Response $recaptchaResponse */
$recaptchaResponse = no_captcha()->verify(strval($request->input('g-recaptcha-response')));
if (!$recaptchaResponse->isSuccess()) {
return true;
}
if ($recaptchaResponse->getScore() <= 0.1) {
return true;
}
return false;
}
今回は下記いずれかが当てはまった場合にエラーとしています。
-
$recaptchaResponse->isSuccess()
がFalse -
$recaptchaResponse->getScore()
が0.1以下
まとめ
reCAPTCHAは比較的工数をかけずにフォームの不正送信を防ぐことができるのが魅力です。
他にも代表的な実装例としてログインフォームが挙げられます。
最後までお読みいただきありがとうございました!
参考記事
私たち BABY JOB は、子育てを取り巻く社会のあり方を変え、「すべての人が子育てを楽しいと思える社会」の実現を目指すスタートアップ企業です。圧倒的なぬくもりと当事者意識をもって、こどもと向き合う時間、そして心のゆとりが生まれるサービスを創出します。baby-job.co.jp/
Discussion