網路上總會有人為了一己私利,透過機器人(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
https://crates.io/crates/rocket-recaptcha-v3
Cargo.toml
rocket-recaptcha-v3 = "*"
使用方法
註冊網站並取得reCAPTCHA v3的兩個金鑰
首先來到這個頁面來註冊要使用reCAPTCHA v3的網站。
然後會得到兩個金鑰,請記錄下來。
將reCAPTCHA v3的兩個金鑰加進Rocket的設定檔中
接著將剛才得到的reCAPTCHA v3的兩個金鑰,加進Rocket的設定檔Rocket.toml
中的recaptcha
欄位下的v3
欄位。這個v3
欄位下有一個必填欄位secret_key
,即用來「建立網站和 reCAPTCHA 之間的通訊」的金鑰。還有一個選填欄位html_key
,即「向使用者顯示的 HTML 程式碼中」使用的金鑰。
舉例來說:
[global.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;
let rocket = rocket::ignite().attach(ReCaptcha::fairing());
驗證reCAPTCHA v3的記號
rocket_recaptcha_v3
這個crate中的ReCaptchaToken
結構體,可以作為參數守衛、查詢守衛或是實作了data::FromData
特性的結構體的欄位。我們可以呼叫ReCaptcha
結構實體的verify
方法,透過參數傳入ReCaptchaToken
結構實體的參考,和一個選用的ClientRealAddr
結構實體參考,來驗證使用者的真人程度。
例如:
#[macro_use]
extern crate rocket;
use rocket::State;
use rocket::request::Form;
use rocket::response::Redirect;
use rocket_recaptcha_v3::{ReCaptcha, ReCaptchaToken};
#[derive(Debug, FromForm)]
struct LoginModel {
username: String,
password: String,
recaptcha_token: ReCaptchaToken,
}
#[post("/login", data = "<model>")]
fn login_post(recaptcha: State<ReCaptcha>, model: Form<LoginModel>) -> Result<Redirect, Redirect> {
match recaptcha.verify(&model.recaptcha_token, None) {
Ok(verification) => {
...
}
Err(err) => {
...
}
}
}
驗證真人
若reCAPTCHA v3的記號驗證成功,ReCaptcha
結構實體的verify
方法會回傳一個ReCaptchaVerification
結構實體,其score
欄位即為分數值。用if
結構來判斷分數值的高低來決定後續要做的動作。
例如:
#[post("/login", data = "<model>")]
fn login_post(recaptcha: State<ReCaptcha>, model: Form<LoginModel>) -> Result<Redirect, Redirect> {
match recaptcha.verify(&model.recaptcha_token, None) {
Ok(verification) => {
if verification.score > 0.7 {
...
} else {
...
}
}
Err(err) => {
...
}
}
}
完整的範例可以查看「reCAPTCHA v3 for Rocket Framework」專案中的examples
目錄。