在 Laravel 中使用 Google 的 reCAPTCHA
CAPTCHA,全稱為:
全自動區分電腦和人類的公開圖靈測試
(Completely Automated Public Turing test to tell Computers and Humans Apart)
我們比較熟悉的叫法應該是驗證碼,是一種用來判斷使用者是否為真人還是機器人 (Bot) 的一種方式。
CAPTCHA 後來改版成 reCAPTCHA,並被 Google 收購。在網站中使用 CAPTCHA,目的是為了避免網站被惡意的機器人攻擊。像是註冊多組帳號,以此濫用服務或是製造大量留言。
Google 的 reCAPTCHA 分為兩個版本,分別是 v2 與 v3。v2 對常上網的人來說可能很眼熟,因為各大網站的登入可能都會有。
v2 這種驗證碼雖然可以有效的防止機器人,但對用戶來說,其實體驗上並不算上友好。如果你被認定為機器人,你就要開始認識外國的紅綠燈、汽車或是斑馬線。
為了增進使用者體驗,Google 後來推出了 v3。v3 會利用 AI 去主動分析使用者的行為,並給一個分數。當分數太低時,就會被認為是機器人,並開始認識紅綠燈的測驗。
如果網站有 v3,你可能會在網站右下角發現這個。
這個分數可以由自己去設定,但是如果分數設定不對,使用者可能在瀏覽網站時,可能時不時就要開始玩認識紅綠燈的測驗,導致使用者體驗更差。
這也是為什麼 v3 無法完全取代 v2 的原因。
在 Laravel 中使用 reCAPTCHA
前面稍微介紹 Google reCAPTCHA 。接下來進本次文章重點,在 Laravel 中會該如何使用 Google reCAPTCHA v2?
首先我們要先上 Google reCAPTCHA 的網站申請 API key。申請完之後會取得 Site Key 與 Secret Key。
將這兩個 key 記錄在 Laravel 的 .env
中,之後會在 Config File 中取用。
然後我們在 config/services.php
中設定。
<?php
return [
'recaptcha_site_key' => env('RECAPTCHA_SITE_KEY'),
'recaptcha_secret_key' => env('RECAPTCHA_SECRET_KEY'),
];
假設我們要在網站的登入中使用 reCAPTCHA,根據官方文件,我們要在 <head>
中引入 API 的 JavaScript Script,並在登入的 form 中插入 reCAPTCHA。
<html>
<head>
<title>reCAPTCHA demo: Simple page</title>
<!-- 引入 reCAPTCHA 的 JS 檔案 -->
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
</head>
<body>
<form action="?" method="POST">
<!-- 表單中加入驗證的區域 -->
<div class="g-recaptcha" data-sitekey="your_site_key"></div>
<br/>
<input type="submit" value="Submit">
</form>
</body>
</html>
後台的部分,Request 接收到的 name
是 g-recaptcha-response
。
這裡我們會使用 Laravel 提供的 Rule
功能,自訂一個 Recaptcha 的 Rule 來驗證 reCAPTCHA 的值是否正確。下一步會說明如何建立這個 Rule。
<?php
// 自己定義一個 Recaptcha 的 Rule 來檢查驗證碼
use App\Rules\Recaptcha;
protected function validateLogin(Request $request)
{
$request->validate([
$this->username() => 'required|string',
'password' => 'required|string',
// 在 validate 使用自定義的 Rule
'g-recaptcha-response' => ['required', new Recaptcha],
], [
'g-recaptcha-response.required' => '請完成驗證',
]);
}
接下來我們要建立一個 Recaptcha
的 Rule,首先輸入指令。
php artisan make:rule Recaptcha
這時候就會在 app 底下新增一個 Rule/Recaptcha.php
。在 Recaptcha.php
中,我們會使用 Laravel 提供的 HTTP 客戶端工具 (使用 Guzzle 套件),發送一個 POST 請求至 google reCAPTCHA 的 API 進行驗證。
注意這裡請求的數據類型必須使用 application/x-www-form-urlencoded
因此在創建請求時,需要調用 asForm()
方法。
<?php
public function passes($attribute, $value)
{
$verifyUrl = 'https://www.google.com/recaptcha/api/siteverify';
$response = Http::asForm()->post($verifyUrl, [
'secret' => config('services.recaptcha_secret_key'),
'response' => $value,
]);
// 取得 response 的 success 值
return $response->json()['success'];
}
返回的 $response
為 JSON 格式,這裡使用 json()
方法轉成 Array 格式。
[
"success" => true
"challenge_ts" => "2020-09-21T12:31:20Z"
"hostname" => "website.com"
]
陣列中的 success
為布林值,如果為 true
,代表驗證成功。如此一來這樣就可以在登入頁面中使用 reCAPTCHA 來防止惡意機器人了。
這邊可能會有人有疑惑,為什麼不直接在
app\Rules\Recaptcha.php
中使用env()
去拿 API Key 就好。反而先在 config 檔案中設定,在使用config()
去拿 API key。這是因為,如果專案上到正式環境,我們通常會使用
php artisan config:cache
這個指令來幫 config 設定進行快取。但只要一進行這個動作,在非 config 的檔案中 (controller 或是 blade),使用
env()
都只會拿到空值。Laravel 的文件中也有提到這一點。