在 Laravel 中使用 Google 的 reCAPTCHA

程式技術
sharkHead

CAPTCHA,全稱為:

全自動區分電腦和人類的公開圖靈測試
(Completely Automated Public Turing test to tell Computers and Humans Apart)

我們比較熟悉的叫法應該是驗證碼,是一種用來判斷使用者是否為真人還是機器人 (Bot) 的一種方式。

CAPTCHA 後來改版成 reCAPTCHA,並被 Google 收購。

在網站中使用 reCAPTCHA,目的是為了避免網站被惡意的機器人攻擊。像是註冊多組帳號,以此濫用服務或是製造大量留言。

Google 的 reCAPTCHA 分為兩個版本,分別是 v2 與 v3。

v2 對常上網的人來說可能很眼熟,因為各大網站的登入可能都會有。

2021_07_14_16_47_08_60eea48c2c552.png
登入頁面下方很常出現的 「我不是機器人」

v2 這種驗證碼雖然可以有效的防止機器人,但對用戶來說,其實體驗上並不算上友好。

如果你被認定為機器人,你就要開始認識外國的紅綠燈、汽車或是斑馬線。

2021_07_14_16_47_21_60eea499b3eae.jpg
請點選有紅綠燈的區塊

為了增進使用者體驗,Google 後來推出了 v3。v3 會利用 AI 去主動分析使用者的行為,並給一個分數。當分數太低時,就會被認為是機器人,並開始認識紅綠燈的測驗。

如果網站有 v3,你可能會在網站右下角發現這個。

2021_07_14_16_47_31_60eea4a38377b.jpg
有這個就代表網站有使用 v3

這個分數可以由自己去設定,但是如果分數設定不對,使用者可能在瀏覽網站時,可能時不時就要開始玩認識紅綠燈的測驗,導致使用者體驗更差。

這也是為什麼 v3 無法完全取代 v2 的原因。

在 Laravel 中使用 reCAPTCHA

前面稍微介紹 Google reCAPTCHA 。接下來進本次文章重點,在 Laravel 中會該如何使用 Google reCAPTCHA v2?

首先我們要先上 Google reCAPTCHA 的網站申請 API key。申請完之後會取得 site key 與 secret key。

將這兩個 key 記錄在 Laravel 的 .env 中,之後會在 config file 中取用。

2021_07_14_16_47_43_60eea4afb9703.png
敏感資料建議都寫在 .env 中

然後我們在 config/services.php 中設定。

<?php

return [
    'recaptcha_site_key' => env('RECAPTCHA_SITE_KEY'),
    'recaptcha_secret_key' => env('RECAPTCHA_SECRET_KEY'),
];

假設我們要在網站的登入中使用 reCAPTCHA,根據官方文件。

我們要在 <head>  中引入 API 的 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 的文件中也有提到這一點。

sharkHead
written by
sharkHead

後端打工仔,在下班後喜歡研究各種不同的技術。稍微擅長 PHP,並偶爾涉獵前端開發。個性就像動態語言般隨興,但渴望做事能像囉嗦的靜態語言那樣嚴謹。

0 則留言