用 reCAPTCHA v2 來做非機器人驗證

用 reCAPTCHA v2 來做非機器人驗證
用 reCAPTCHA v2 來做非機器人驗證

本篇要解決的問題

前端做的事情都是明碼,而在瀏覽器愈來愈強大的情況下,工程師們很容易可以看出來這個功能背後呼了什麼 API、帶了什麼參數。而如果被看透了,有些活動就很容易被用程式碼大量執行,比方說:投票。

一般來說,投票活動就是為了增加會員數,或是拿參加者的名單,但,有些時候,PM 死都不肯加入會員功能進去,想讓全世界的人看到頁面想投就投,

然後還要求每個人只能投幾票。

對,不用會員,然後要限制投票數。

用了會員,就可以由後端紀錄會員行為,知道他已經投了幾票。

不用會員,前端就只有 cookies 了,偏偏清 cookies 又很方便,基本上使用者自己清 cookies,前端就沒有了判斷依據,使用者想投幾票就投幾票。

「需要感謝的人太多了,就感謝天吧!」– 陳之藩 先生寫的。

「需要解決的問題太多了,就問 Google 吧!」– August 說的。

這幾天才突然想起,平常要下載一些資料時,都會遇到一個「我不是機器人」的驗證:

reCAPTCHA
reCAPTCHA

叮~ 腦子裡響了一下,即便無法全面防止灌票,但至少可以有效阻擋用程式來灌票的行為。

Google 了一下 reCAPTCHA,發現有 v2、v3 二版,但 v3 版的似乎很容易誤判成機器人。

而 v2 的「隱形 reCAPTCHA 標記」,感覺與其用隱形,不如直接用 v3。

因此本篇主要先來研究 v2 的「我不是機器人」核取方塊。

本篇最後會完成的 Demo 在這:

https://letswritetw.github.io/letswrite-recaptcha-v2/

最後一段會附上 Demo 頁原始碼的網址。

2020.11.18 補充:
今日完成了關於 v3 的筆記文,連結在這:
https://letswrite.tw/recaptcha-v3/


註冊 reCAPTCHA

登入了 Google 帳號後,進到 reCAPTCHA 的頁面:Google reCAPTCHA

點選右上角的「Admin Console」:

點擊 Admin Console
點擊 Admin Console

會出現一張「註冊新網站」的表單:

註冊新網站 的表單
註冊新網站 的表單

「標籤」就是取一個以後我們認得出是哪個站在用的名字。

「reCAPTCHA 類型」選擇「reCAPTCHA v2」,接著會出現一個小選單,選擇「我不是機器人」核取方塊:

類型選 reCAPTCHA v2 > 我不是機器人
類型選 reCAPTCHA v2 > 我不是機器人

「網域」是白名單的意思,要在這邊填寫的網域下才可以使用 reCAPTCHA 功能。

Google reCAPTCHA 並不是全免費的產品,不過不用緊張,因為免費的額度是 1 個月內 1 百萬次 XD~

但還是要限白名單較安全,因為金鑰會是明碼,全世界都看得到。

「傳送通知給擁有者」就是指當有可疑活動時,會主動收到通知信:

傳送通知給擁有者說明
傳送通知給擁有者說明

以上都填寫完,接受服務條款也勾選了,就可以按下「提交」。

按下完提交,註冊成功就會看到頁面顯示了:金鑰、密鑰。

取得金鑰及密鑰
取得金鑰及密鑰

金鑰是前端頁面使用,密鑰是後端在 server 使用。

本篇會使用 Google Apps Script 來寫後端。

把這邊的金鑰跟密鑰都記下來,後面幾段都會用到。

忘記了也沒關係,因為 reCAPTCHA 後台 都看得到。


頁面使用 reCAPTCHA 方法 1:直接埋 HTML

參考文件:Automatically render the reCAPTCHA widget

前端頁面要放上「我不是機器人」的那塊有二種方法,第一種就是直接在想放的地方放上 HTML。

首先,頁面上先引用 JS:

<script src="https://www.google.com/recaptcha/api.js" async defer></script>

接著,在想放的地方放上以下的程式碼:

除了 data-sitekey 為必填,其他都是選填,可放可不放,各 data-* 說明如下:

  • class 必須為「g-recaptcha」
  • data-sitekey,填入「網站金鑰」
  • data-theme,亮色或深色樣式:dark、light。預設是 light
  • data-size,大小:compact、normal。預設是 normal
  • data-callback,驗證完成後要觸發哪個 function,會回傳 g-recaptcha-response token
  • data-expired-callback,當驗證過期後會觸發哪個 function
  • data-error-callback,當驗證失敗時會觸發哪個 function

data-callback 裡面填的是 function 名稱,會自動帶一組 token,這 token 就是可以送到後端,讓後端拿 token 去跟 reCAPTCHA 做驗證,所以建議是必填。

上面的範例是寫 data-callback="verifyCallback" 因此當使用者打勾後,就會呼 verifyCallback 這個 function,因此我們可以寫:

function verifyCallback(token) {
  // 把 token 送到後端
}

除了 token,還可以額外送出使用者的 IP,後端需不需要 IP 非必要,但 token 就是必要的。

整個 callback 在本篇的 demo,程式碼如下:

uriGAS 是在 Google Apps Script 部署為網路應用程式後會取得的 URL,後端收 token 並做驗證的部份,會統一寫在後續的段落中。

data-expired-callback 當使用者勾選了「我不是機器人」後,在 callback 中產生的 token 存活時間是 2 分鐘,2 分鐘內必須傳給後端讓後端做驗證。二分鐘一到 token 便會失效,並觸發 data-expired-callback 中我們填寫的 function,打勾的樣式也會變成紅字,像這樣:

驗證程序到期
驗證程序到期

頁面使用 reCAPTCHA 方法 2:寫 JavaScript 使用

參考文件:Explicitly render the reCAPTCHA widget

第二種方法,就是先在要放置 reCAPTCHA 的地方,放一個空的 div,接著用 JS 把 reCAPTCHA 的打勾按鈕給塞進去。

首先,一樣要先引用 JS:

<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit" async defer></script>

跟方法 1 不一樣,在引用 JS 時就要先帶入參數,參數一共有 3 個:

  • onload:api.js 載入完後要執行哪個 function
  • render:固定填 explicit
  • hl:語系,非必填,reCAPTCHA 會自動檢測。語系列表

onload 就是 api.js 載入完後要執行哪個 function,要執行的就是在指定的 div 上塞入 reCAPTCHA。程式碼如下:

render 有的參數跟方法 1 中的 data-* 相同,說明如下:

  • sitekey,填入「網站金鑰」
  • theme,亮色或深色樣式:dark、light。預設是 light
  • size,大小:compact、normal。預設是 normal
  • callback,驗證完成後要觸發哪個 function,會回傳 g-recaptcha-response token
  • expired-callback,當驗證過期後會觸發哪個 function
  • error-callback,當驗證失敗時會觸發哪個 function

callback 跟方法 1 的 callback 相同,都是執行把收到的 token 傳給後端做驗證,verifyCallback 這個 function 這邊就不再重寫。


後端向 reCAPTCHA 驗證

參考文件:Verifying the user’s response

我們一開始在註冊好帳號後,除了拿到頁面要的金鑰,也會拿到一組後端用的密鑰。

這組密鑰就是後端向 reCAPTCHA 驗證時會用到的。

前面寫的二種方法,最後都有寫一個 callback 把拿到的 token 傳來,後端要做的,就是把拿到的 token,以及密鑰,POST 到 reCAPTCHA 指定的 URL,就會收到回應,看是成功或失敗。

本篇是拿 Google Apps Script 當後端。

登入 Google 帳號後,到 Google 雲端硬碟,新增一個「Google Apps Script」的檔案:

新增 GAS 檔案
新增 GAS 檔案

(以下將 Google Apps Script 簡稱 GAS)

新增後先存檔,接著整個寫程式碼的部份改成以下:

doPost 指的是這個 GAS 收到前端 POST 時要執行的。

var ip = params.ipremoteip: ip 這二行,如果前端沒有給 IP 的話就要刪掉。

最後的 return ContentService.create...... 就是把從 reCAPTCHA 拿到的結果,回傳給前端。

結果會是 JSON,有的值如下:

{
  "success": true|false,
  "challenge_ts": yyyy-MM-dd'T'HH:mm:ssZZ, 
  "hostname": string,
  "error-codes": [...] // optional
}

successfalse 的情況目前知道的有二:token 已被用過、token 的存在時間超過 2 分鐘。

目前測大部份都是 true,有可能判定是機器人時會回傳 false 吧?


取 IP

因為後端在 POST 時可以帶上 IP,因此本篇的 Demo 有取 IP 的部份。

這邊用的是:

https://www.cloudflare.com/cdn-cgi/trace

這個 URL,用 GET 回來時會是字串,裡面就會有 IP 值,用一個 for 回圈把 IP 值給撈出來就行了。


Demo 及原始碼

本篇的 Demo 如下:

https://letswritetw.github.io/letswrite-recaptcha-v2/

方法 1、方法 2、GAS 部份,這三項都有,分成三頁。

頁面上就直接會有「我不是機器人」的區塊。

直接看頁面原始碼可以看到 HTML 及 JS。

或是可以到 Github 上看整個 Demo 的原始碼:

https://github.com/letswritetw/letswrite-recaptcha-v2

Summary
用 reCAPTCHA v2 來做非機器人驗證
Article Name
用 reCAPTCHA v2 來做非機器人驗證
Description
本篇大綱:本篇要解決的問題。註冊 reCAPTCHA。頁面使用 reCAPTCHA 方法 1:直接埋 HTML。頁面使用 reCAPTCHA 方法 2:寫 JavaScript 使用。後端向 reCAPTCHA 驗證。取 IP。
Augustus
Let's Write
Let's Write
https://letswrite.tw/wp-content/uploads/2020/08/logo_512.jpg
訂閱
通知
guest

0 Comments
Inline Feedbacks
看所有留言