網路上總會有人為了一己私利,透過機器人(robot)來快速填寫並送出網頁上的表單,導致該網站的帳號密碼被破解,或者販賣的商品立刻被有心人士一掃而空(倒買倒賣),又或是資料庫的資料在短時間內被全部爬出來。為了防範機器人來我們的網站上搗亂,最好將Google提供的reCAPTCHA真人驗證機制加入網站中。
CAPTCHA給人的感覺就是要讓使用者去完成艱難的圖形任務,或是回答一些古怪的問題,來證明使用者自己是真人。然而Google第三代的reCAPTCHA已經完全不需再讓使用者傷腦、傷眼睛了!第三代的reCAPTCHA甚至不需要使用者用滑鼠進行任何「勾選」或是「滑動」的動作,就可以判斷使用者是否為真人。
reCAPTCHA v3會利用範圍0.0~1.0的分數值來表示使用者可能比較像機器人還是可能比較像真人,數值愈高愈像真人。Google官方雖然建議將0.5
以上的值當作真人,但實測時真人似乎都是0.9
以上,所以為了擋掉更多像人的機器人,我們其實可以選用比0.5
更高的數值作為門檻。
除了單純以分數值來區分機器人與真人之外,我們也可以實作額外的商業邏輯來更進一步地驗證使用者的身份。例如,使用者雖然用正確的帳號和密碼成功登入帳號,但是他的reCAPTCHA v3分數值只有0.4
或是0.5
時,我們可以要求他去信箱收驗證信才能繼續登入。
此外,reCAPTCHA v3還提供了action
欄位,可以方便我們分析訪客們的行為。
reCAPTCHA v3 for Rocket Framework
「reCAPTCHA v3 for Rocket Framework」是筆者開發的套件,可以幫助開發者將reCAPTCHA v3加進Rocket應用程式中。
Crates.io
Cargo.toml
使用方法
註冊網站並取得reCAPTCHA v3的兩個金鑰
首先來到這個頁面來註冊要使用reCAPTCHA v3的網站。
然後會得到兩個金鑰,請記錄下來。
將reCAPTCHA v3的兩個金鑰加進Rocket的設定檔中
接著將剛才得到的reCAPTCHA v3的兩個金鑰,加進Rocket的設定檔Rocket.toml
中的recaptcha
欄位下的v3
欄位。這個v3
欄位下有一個必填欄位secret_key
,即用來「建立網站和 reCAPTCHA 之間的通訊」的金鑰。還有一個選填欄位html_key
,即「向使用者顯示的 HTML 程式碼中」使用的金鑰。
舉例來說:
[default.recaptcha.v3]
html_key = "6Lf6dLIUAAAAAAxghN7nH6m_yuLfHwdD3N7FpanR"
secret_key = "6Lf6dLIUAAAAAHdJ4e0nsv-8OpFH-7Oad1XQ95rq"
這個html_key
欄位主要是給開發者自己用的,可以利用HTML模板引擎將html_key
欄位的值填入HTML中。
將reCAPTCHA v3加入HTML中
預設的reCAPTCHA v3徽章(badge)會被浮動放置在網頁的右下角,挺難看的。所以此處簡單介紹用內聯(inline)的方式來加入reCAPTCHA v3的徽章。
首先在適當的位置上加入:
<script src="https://www.recaptcha.net/recaptcha/api.js?render=explicit&&onload=onReCaptchaLoadCallback" defer></script>
<script>
function onReCaptchaLoadCallback() {
var clientId = grecaptcha.render('inline-badge', {
'sitekey': 'recaptcha_key',
'badge': 'inline',
'size': 'invisible'
});
grecaptcha.execute(clientId, {action: 'action_name'}).then(function (token) {
// save the token
});
}
</script>
注意這邊引用JavaScript程式時使用的網域為www.recaptcha.net
而不是www.google.com
,這是為了讓中國地區也可以使用reCAPTCHA v3。而在其後的JavaScript程式碼中,recaptcha_key
的地方要填入html_key
欄位的值。action_name
的值則設訂一個自己看得懂的名稱就好了,方便用來分析數據。inline-badge
則是要用來顯示reCAPTCHA v3徽章的元素ID。
當reCAPTCHA v3執行成功後,會回傳一個記號(token
),要把這個token
的值記下來,之後還要跟著表單內容一同傳給我們的Web應用程式來驗證。
將reCAPTCHA v3加入Rocket中
rocket_recaptcha_v3
這個crate中的ReCaptcha
結構體,提供了fairing
關聯函數,可以回傳一個整流片。這個整流片會在Rocket的Attach
階段,讀取Rocket.toml
設定檔中剛才介紹的secret_key
和html_key
欄位,來建立ReCaptcha
結構體的實體,並註冊給Rocket作為應用程式狀態。
use rocket_recaptcha_v3::ReCaptcha;
...
rocket::build().attach(ReCaptcha::fairing())
驗證reCAPTCHA v3的記號
rocket_recaptcha_v3
這個crate中的ReCaptchaToken
結構體,可以作為參數守衛、查詢守衛或是實作了data::FromData
特性的結構體的欄位。我們可以呼叫ReCaptcha
結構實體的verify
方法,透過參數傳入ReCaptchaToken
結構實體的參考,和一個選用的ClientRealAddr
結構實體參考,來驗證使用者的真人程度。
例如:
#[macro_use]
extern crate rocket;
use rocket::{form::Form, response::Redirect, State};
use rocket_recaptcha_v3::{ReCaptcha, ReCaptchaToken};
#[derive(Debug, FromForm)]
struct LoginModel {
username: String,
password: String,
recaptcha_token: ReCaptchaToken,
}
#[post("/login", data = "<model>")]
async fn login_post(
recaptcha: &State<ReCaptcha>,
model: Form<LoginModel>,
) -> Result<Redirect, Redirect> {
match recaptcha.verify(&model.recaptcha_token, None).await {
Ok(verification) => {
...
},
Err(err) => {
...
},
}
}
驗證真人
若reCAPTCHA v3的記號驗證成功,ReCaptcha
結構實體的verify
方法會回傳一個ReCaptchaVerification
結構實體,其score
欄位即為分數值。用if
結構來判斷分數值的高低來決定後續要做的動作。
例如:
#[post("/login", data = "<model>")]
async fn login_post(
recaptcha: &State<ReCaptcha>,
model: Form<LoginModel>,
) -> Result<Redirect, Redirect> {
match recaptcha.verify(&model.recaptcha_token, None).await {
Ok(verification) => {
if verification.score > 0.7 {
...
} else {
...
}
},
Err(err) => {
todo!();
},
}
}
完整的範例可以查看「reCAPTCHA v3 for Rocket Framework」專案中的examples
目錄。