短信驗證的設計
在業務中,為安全起見,常常需要二次驗證。其流程一般為,密碼驗證之后,需要再次輸入一個一次性的密碼(一般為位數較短的數字),才能完成驗證。二次驗證一般需要同時驗證設備的***性。
從驗證方式上看,二次驗證可以通過短信驗證或 APP 驗證(Google Authenticator)。對于短信驗證,優點是操作方便,不需安裝額外的 APP;缺點是實時性較差,依賴短信,有被竊聽的風險。APP 方式正好相反。
google authenticator
以此來看,在業務初期,宜使用短信驗證方式,減少用戶輸入的復雜度。待用戶量和用戶黏度上去之后可以考慮換用 APP 驗證。畢竟,在已經安裝 APP 的情況下,這種方式更加便捷和安全。
OTP(one time password),即一次性驗證密碼,也稱動態口令。一次性密碼的產生方式,主要是以時間差做為服務器與密碼產生器的同步條件。在需要登錄的時候,就利用密碼產生器產生一次性密碼,OTP 一般分為計次使用(HOTP)以及計時使用(TOTP)兩種,計次使用的 OTP 產出后,可在不限時間內使用;計時使用的 OTP 則可設定密碼有效時間,從 30 秒到兩分鐘不等,而 OTP 在進行認證之后即廢棄不用,下次認證必須使用新的密碼,增加了試圖不經授權存取有限制資源的難度1。
Google Authenticator 的二次驗證也是使用的 OTP。其核心原理是:服務器和客戶端(一般是Google Authenticatorapp)保存同一份密鑰,客戶端根據該密鑰和當前時間戳計算得到 6 位數字,發到服務器,服務器根據同樣的算法得到 6 位數字。做比較之后判斷客戶端傳來的數字是否合法。詳述如下2:

Google Authenticator View
對于 HOTP,客戶端和服務器事先協商好一個密鑰 K,用于一次性密碼的生成過程,此密鑰不被任何第三方所知道。此外,客戶端和服務器各有一個計數器 C,并且事先將計數值同步。
進行驗證時,客戶端對密鑰和計數器的組合(K,C)使用 HMAC(Hash-based Message Authentication Code)算法計算一次性密碼,公式如下:
HOTP(K,C)=Truncate(HMAC_SHA1(K,C))
經過截斷,得到的 OTP 一般是 6 位數,用戶將得到的 OTP 發送到服務器,服務器端經過同樣的驗證,驗證成功則計數器加 1。
TOTP 將 HOTP 中的計數器 C 用當前時間來替代,于是就得到了隨著時間變化的一次性密碼。也就是說,當給定密鑰之后,每一時刻的 OTP 都是固定不變的(假設間隔時間固定為 T)。
這里需要稍加注意的地方是間隔時間的選擇,不能太長,否則安全性不能保證;也不能太短,否則用戶無法及時驗證。Google 的選擇是 30 秒。當我們使用 Google Authenticator 驗證時,因為 OTP 的獲得沒有延遲,因此 30 秒是個比較合理的數字;如果我們想要通過短信進行驗證,考慮到短信推送的延遲和用戶讀取的延遲,30 秒的時間可能有點短。通常情況下短信驗證的有效時間在 1 分鐘到 2 分鐘之間,這是一個比較合理的數字。
TOTP 還有個邊界問題,如下圖所示。當在 OA 時刻發起短信驗證請求,在理想情況下,我們只有在 OA 時間段發起驗證,驗證才能成功。也就是說,用戶的有效輸入時間在 T(當發起時刻靠近 O 時)到 0 之間(當發起時刻靠近 A),也就是說,用戶有一定的概率驗證幾乎必然失敗(發起時刻靠近 A,考慮到 OTP 獲取延遲和用戶輸入延遲),這顯然是不合理的。

otp verification
可以通過在驗證時刻驗證當前時刻以及前 N 個時間片來解決這個問題。如圖所示,我們驗證當前時刻和前一時刻,當在 OA 時間片發起驗證時,驗證區間是 OB 段,這樣用戶的有效輸入時間就是 T~2T,如果我們取 T 為 1 分鐘,這會是一個比較合理的結果。
真實情況下還要考慮客戶端和服務器的時鐘偏差,不再贅述。(如果使用短信驗證,基本不存在這個問題,因為發起驗證的 OTP 也是由服務器產生的。)