iT邦幫忙

2025 iThome 鐵人賽

DAY 7
0
Modern Web

不只是登入畫面!一起打造現代化登入系統系列 第 7

房門與門鎖[ 4 / 6 ]:表單再升級 — reCAPTCHA 與密碼強度驗證

  • 分享至 

  • xImage
  •  

在上一篇我們完成了登入頁的組件化設計,讓程式碼更易於維護。既然基礎結構已經打好,接下來就能專心加入更進階的功能了🥳。
這一篇,我們要把表單驗證的部分徹底完成,延續 房門與門鎖[ 2 / 6 ]:React 生態系導入 — 表單驗證 & Router 分頁 的簡易表單驗證,加入 reCAPTCHA 驗證密碼強度檢測

完整的表單驗證

https://ithelp.ithome.com.tw/upload/images/20250921/20110586HFoUKHLu37.png

本篇重點整理:

  • reCAPTCHA: 避免機器人濫用,確保請求來源是真人
  • 密碼強度驗證: 即時顯示密碼強弱,提升使用者安全意識

reCAPTCHA

「我不是機器人🤖」這個詞是不是很熟悉呢?
這是 Google 開發的防機器人驗證功能,目前最常見的就是 reCAPTCHA v2 ( 核取方框 ),以及 v3 ( 隱形驗證 )

它主要解決了什麼問題?

  • 帳號安全 — 防止惡意機器人自動註冊帳號或暴力登入
  • 內容品質 — 避免垃圾留言與垃圾請求攻擊
  • 系統效能 — 減少伺服器的異常流量與資源消耗

我們立馬加入進來吧!

首先進入 Google reCAPTCHA 網站,登入後申請建立新網站:
https://ithelp.ithome.com.tw/upload/images/20250921/20110586QaoPf7yj15.png

  • 勾選 v2 的「我不是機器人」核取方塊
  • 網域名稱輸入自己的開發位址,例如本機測試就填 localhost

申請完成後會得到 Site Key (公開金鑰)Secret Key (秘密金鑰),兩者用途不同:

  • Site Key (公開金鑰) → 放在前端程式碼中,用來生成驗證框。
  • Secret Key (秘密金鑰) → 僅限後端使用,用來向 Google 驗證 token 是否有效。

為什麼 Secret Key 不能公開?

如果把秘密金鑰放到前端或公開到 GitHub,會有以下風險:

  1. 惡意繞過驗證 — 攻擊者可以偽造驗證通過的結果,直接註冊假帳號或暴力登入。
  2. 濫用 API 配額 — 別人可以拿你的金鑰狂發驗證請求,消耗掉配額,造成服務異常。
  3. 拖累系統風評 — Google 可能因為異常流量,暫時封鎖你的金鑰,導致正常用戶無法使用。

金鑰該放哪裡?

公開金鑰 (Site Key) → 安全地放在前端 React 元件中。
秘密金鑰 (Secret Key) → 放在後端環境變數,只用於伺服器與 Google API 溝通。

常見做法:

  • 本地開發:放在 .env 檔案,例如:
RECAPTCHA_SECRET_KEY=your-key

並在 .gitignore 中忽略 .env,避免 push 到 GitHub。

  • 正式佈署:使用雲端平台 ( 如 Vercel、Netlify、Firebase ) 提供的 Environment Variables 來儲存,永遠不要硬寫進程式碼中。

密碼強度驗證

就算通過 reCAPTCHA 機器人驗證,如果使用者輸入一個「123456」當密碼,那系統依然非常危險。
密碼強度驗證的目的是在使用者輸入時,立即提示密碼的安全等級,通常會依據以下規則檢測:

  • 長度:通常建議至少 8–12 碼
  • 字母:必須包含大小寫字母
  • 數字:必須至少一個數字
  • 特殊字元:如 !@#$%^&*
    這樣可以在「前端」就提醒使用者,讓密碼不會過於簡單。這也是為什麼在之前的簡易驗證裡會這麼寫
// validSchema.ts 部分程式碼
const passwordRegex = /^[a-zA-Z0-9!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~]+$/;

常見做法有兩種:

  1. 自訂規則檢測 ( 基於 Regex 與條件判斷 )
    我們可以透過上述條件來計算分數:
type PasswordStrength = "None" | "Weak" | "Moderate" | "Strong" | "VeryStrong";

const checkPasswordStrength = (password: string): PasswordStrength => {
  let strength = 0;
  if (password.length >= 8) strength += 1; // 長度至少8
  if (password.length >= 12) strength += 1; // 長度至少12
  if (/[a-z]/.test(password)) strength += 1; // 包含小寫字母
  if (/[A-Z]/.test(password)) strength += 1; // 包含大寫字母
  if (/[0-9]/.test(password)) strength += 1; // 包含數字
  if (/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~]/.test(password)) strength += 1; // 包含特殊字元

  if (password.length === 0) return "None";
  if (strength <= 2) return "Weak";
  if (strength <= 4) return "Moderate";
  if (strength <= 5) return "Strong";
  return "VeryStrong";
};

💡優點:快速、無需額外套件。
⚠️缺點:僅檢查格式,無法判斷密碼是否「常見弱密碼」。

  1. 使用專業工具 — zxcvbn.js
    如果想要更精準的檢測,可以使用 Dropbox 開發的 zxcvbn.js。
    它會根據字典資料與統計模型,回傳 0–4 分數與破解時間估算,比單純 Regex 更實際。
import zxcvbn from "zxcvbn";

const result = zxcvbn("MyP@ssw0rd!2025");

console.log(result.score); // 0~4,越高越安全
console.log(result.feedback.suggestions); // 改進建議

這樣驗證不是會很危險嗎?

答案是 會的,如果只有這樣的話🚨,因為:

  • 前端驗證可被繞過
    攻擊者可以直接用 PostmancURL 發送請求,完全跳過前端的正則檢查。

  • Regex 只能檢查「格式」,不能檢查「強度」
    例如 aaaaaaaa 符合正則,卻依然是超弱密碼。

  • 真正的安全驗證必須在後端進行
    後端應該再次檢查密碼規則,確保使用者無法繞過。

所以前端密碼強度檢查的意義在於:
✅ 提升使用者體驗(即時提示)
✅ 教育使用者設定更安全的密碼
❌ 不是安全保障的最終防線

最佳實踐是「前端做提示 + 後端做嚴格驗證」,兩邊都要有。


程式碼明日附上


上一篇
房門與門鎖[ 3 / 6 ]:模組化設計 — 拆分 components 實踐 DRY 策略
系列文
不只是登入畫面!一起打造現代化登入系統7
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言