// OTP 安全

OTP 實作最佳實務:驗證碼、過期、限流與降級

一次性密碼看起來很簡單——產生一個數字、傳條簡訊、再校驗它。但帳號被盜的 bug 就藏在細節裡。本指南涵蓋讓 OTP 流程真正安全又好用的那些決策:驗證碼熵值、儲存、過期時間、限流、重試與降級管道。

用真正的熵產生驗證碼

使用密碼學安全的亂數產生器,絕不用可預測的種子或從時間戳記推導的值。六位數通常是安全與易用的平衡;更短的驗證碼需要更嚴格的限流才能保持安全。

絕不傳送兩次相同的驗證碼,也絕不讓驗證碼能從使用者 id 或序列中猜出。簡訊 OTP 的全部安全,都繫於驗證碼在其短暫生命週期內不可預測。

  • 使用 CSPRNG(如 secrets/crypto 函式庫),而非通用 PRNG。
  • 6 位是標準;若用 4 位,就把限流與過期收緊到底。
  • 每次請求一個驗證碼;簽發新碼時使舊碼失效。

安全儲存驗證碼,絕不用明文

把 OTP 當作短命密碼對待。儲存驗證碼的雜湊(連同識別碼與過期),而非原始值,這樣即使資料庫外洩也不會暴露有效驗證碼。用常數時間函式比較以避免時序攻擊,並在驗證碼被使用或過期後立即刪除記錄。

  • 儲存前對驗證碼做雜湊;以常數時間比較。
  • 把每個驗證碼綁定到單一使用者/工作階段與用途。
  • 在成功、過期或達到嘗試上限後刪除。

設定較短的過期時間

驗證碼應存活數分鐘,而非數小時。5–10 分鐘的過期是常見區間:足夠讓延遲的簡訊仍能送達,又短到外洩的驗證碼通常已經作廢。向使用者顯示剩餘有效期,這樣一則慢訊息才不會被讀作壞掉的流程。

!

把短過期與重送冷卻搭配起來,否則訊息延遲時使用者會猛點重送,讓你的簡訊成本翻倍。

在每個維度限流

限流是同時阻止暴力猜碼與濫用你傳送端點(那會花你的錢,還可能讓你的寄件者被標記)的手段。在傳送與校驗兩側,都依手機號碼、依 IP、依帳號限流。

  • 校驗嘗試:對每個驗證碼限制嘗試次數(如 5 次),隨後使其失效並強制重送。
  • 傳送請求:兩次傳送之間設冷卻,並對每個號碼設每日上限。
  • 全域護欄:依 IP 與依帳號限流,鈍化自動化濫用。
  • 在反覆觸及上限時加入指數退避或 CAPTCHA。

處理重試與降級管道

簡訊是盡力而為的,好的流程會假定有些訊息不會送達。在冷卻後提供重送,並在一兩次簡訊失敗後升級到降級管道——朗讀驗證碼的語音撥號、WhatsApp 或電子郵件——這正是託管 Verify API 所自動化的。

讓失敗狀態有人情味:清晰的「沒收到?」路徑、更換號碼的入口,以及反覆失敗後的客服聯絡方式。

  1. 1

    首次嘗試:簡訊

    傳送驗證碼,並啟動帶重送冷卻的可見倒數計時。

  2. 2

    依請求重送

    冷卻後允許重送;簽發新碼時使舊碼失效。

  3. 3

    升級管道

    反覆簡訊失敗後,提供語音或備用管道來交付同一次驗證。

  4. 4

    提供退出

    讓使用者更正號碼或聯絡客服,而不是走進死胡同。

在不削弱安全的前提下改善體驗

一些小處理能提升完成率:使用 WebOTP API 與簡訊自動填入格式,讓行動瀏覽器能讀取驗證碼;保持訊息措辭一致(有些平台會快取受信任的寄件者);訊息裡除了驗證碼與應用程式名稱,絕不放別的。避免在 OTP 訊息中放連結——那會訓練使用者去點擊,並觸發電信業者垃圾過濾。

  • 在行動端支援 WebOTP/一次性驗證碼自動填入。
  • 保持訊息極簡:應用程式名稱 + 驗證碼,無行銷、無連結。
  • 在地化訊息文字,但保持驗證碼格式穩定。

常見問題

OTP 應該有效多久?+

五到十分鐘是常見時間。它足夠熬過延遲的簡訊,又短到外洩的驗證碼通常已經過期。請務必顯示剩餘時間,這樣慢訊息才不會看起來像壞掉的流程。

我應該允許多少次校驗嘗試?+

把每個驗證碼的嘗試上限設在 5 次左右,隨後使其失效並要求重新傳送。再配合傳送與校驗兩側的依號碼、依 IP、依帳號限流,以阻止暴力猜測與傳送端點濫用。

我該在 OTP 訊息裡放連結嗎?+

不要。把 OTP 訊息保持為只有你的應用程式名稱與驗證碼。連結會訓練使用者去點擊、可能透過 referrer 洩漏驗證碼,還常觸發電信業者垃圾過濾而損害送達。

簡訊 OTP 單獨用夠安全嗎?+

簡訊 OTP 相較僅用密碼是有意義的一步提升,但易受 SIM 換卡與攔截影響。對高價值帳號,請提供通行密鑰或 TOTP 等更強因素,並把簡訊當作降級——參見《OTP 替代方案》指南。

EdgeGigs

第一次就把 OTP 流程實作對。

在 EdgeGigs 上聘請經過審核的開發者,為你的網頁或行動應用程式建置安全的驗證碼產生、過期、限流與降級。

在 EdgeGigs 上找開發者