用真正的熵產生驗證碼
使用密碼學安全的亂數產生器,絕不用可預測的種子或從時間戳記推導的值。六位數通常是安全與易用的平衡;更短的驗證碼需要更嚴格的限流才能保持安全。
絕不傳送兩次相同的驗證碼,也絕不讓驗證碼能從使用者 id 或序列中猜出。簡訊 OTP 的全部安全,都繫於驗證碼在其短暫生命週期內不可預測。
- 使用 CSPRNG(如 secrets/crypto 函式庫),而非通用 PRNG。
- 6 位是標準;若用 4 位,就把限流與過期收緊到底。
- 每次請求一個驗證碼;簽發新碼時使舊碼失效。
安全儲存驗證碼,絕不用明文
把 OTP 當作短命密碼對待。儲存驗證碼的雜湊(連同識別碼與過期),而非原始值,這樣即使資料庫外洩也不會暴露有效驗證碼。用常數時間函式比較以避免時序攻擊,並在驗證碼被使用或過期後立即刪除記錄。
- 儲存前對驗證碼做雜湊;以常數時間比較。
- 把每個驗證碼綁定到單一使用者/工作階段與用途。
- 在成功、過期或達到嘗試上限後刪除。
設定較短的過期時間
驗證碼應存活數分鐘,而非數小時。5–10 分鐘的過期是常見區間:足夠讓延遲的簡訊仍能送達,又短到外洩的驗證碼通常已經作廢。向使用者顯示剩餘有效期,這樣一則慢訊息才不會被讀作壞掉的流程。
把短過期與重送冷卻搭配起來,否則訊息延遲時使用者會猛點重送,讓你的簡訊成本翻倍。
在每個維度限流
限流是同時阻止暴力猜碼與濫用你傳送端點(那會花你的錢,還可能讓你的寄件者被標記)的手段。在傳送與校驗兩側,都依手機號碼、依 IP、依帳號限流。
- 校驗嘗試:對每個驗證碼限制嘗試次數(如 5 次),隨後使其失效並強制重送。
- 傳送請求:兩次傳送之間設冷卻,並對每個號碼設每日上限。
- 全域護欄:依 IP 與依帳號限流,鈍化自動化濫用。
- 在反覆觸及上限時加入指數退避或 CAPTCHA。
處理重試與降級管道
簡訊是盡力而為的,好的流程會假定有些訊息不會送達。在冷卻後提供重送,並在一兩次簡訊失敗後升級到降級管道——朗讀驗證碼的語音撥號、WhatsApp 或電子郵件——這正是託管 Verify API 所自動化的。
讓失敗狀態有人情味:清晰的「沒收到?」路徑、更換號碼的入口,以及反覆失敗後的客服聯絡方式。
- 1
首次嘗試:簡訊
傳送驗證碼,並啟動帶重送冷卻的可見倒數計時。
- 2
依請求重送
冷卻後允許重送;簽發新碼時使舊碼失效。
- 3
升級管道
反覆簡訊失敗後,提供語音或備用管道來交付同一次驗證。
- 4
提供退出
讓使用者更正號碼或聯絡客服,而不是走進死胡同。
在不削弱安全的前提下改善體驗
一些小處理能提升完成率:使用 WebOTP API 與簡訊自動填入格式,讓行動瀏覽器能讀取驗證碼;保持訊息措辭一致(有些平台會快取受信任的寄件者);訊息裡除了驗證碼與應用程式名稱,絕不放別的。避免在 OTP 訊息中放連結——那會訓練使用者去點擊,並觸發電信業者垃圾過濾。
- 在行動端支援 WebOTP/一次性驗證碼自動填入。
- 保持訊息極簡:應用程式名稱 + 驗證碼,無行銷、無連結。
- 在地化訊息文字,但保持驗證碼格式穩定。