谷歌驗證器的原理及JS實現

閱讀本篇文章你能夠了解到谷歌驗證器的實現原理,而且能夠本身使用node.js實現支持谷歌驗證器的兩步驗證。html

這兩年發現身邊的不少應用和網站紛紛支持兩步驗證,而且呼籲用戶使用兩步驗證。node

而且發現,除了Apple ID的兩步驗證以外,其它兩步驗證不少能看到谷歌驗證器(Google Authenticator)的身影。git

這讓我產生了濃厚的興趣,到底谷歌驗證器的原理是什麼,我本身能實現一個相似的驗證器嗎?github

什麼是兩步驗證

兩步驗證就是當用戶輸入帳號密碼並驗證成功以後,須要額外輸入一串一次性隨機密碼(通常是4-6位的數字),服務器以此確認登陸者是用戶本人。算法

兩步驗證的類型

  1. 短信驗證
    短信驗證也是咱們最熟悉最經常使用的兩步驗證,可是我認爲短信驗證有有如下幾個缺點:需支付運營商短信費用、短信到達延遲、短信壓根就沒到達而網站不容許你馬上再次重發。相信你也有過等一條短信讓你抓狂的經歷。
  2. 動態密碼器
    玩過網易遊戲的同窗應該知道網易將軍令,還有部分銀行提供的動態密碼器。我記得我之前第一次見到這個東西的時候就以爲很神奇,這東西不用聯網竟然就能驗證用戶。實際上它的實現原理和Google驗證器的實現原理差很少,後面咱們會詳細講到。
  3. 口令卡
    口令卡是我認爲最反人類的設計。曾經辦工商銀行網上銀行的時候就有過一張,它上面是以矩陣的形式排列若干個字符,系統會給你一些座標,要求你根據座標找出相應的字符輸入到系統。😤%&¥#……(*#)我歷來沒用過。不可能的,這輩子不可能用口令卡的。。

兩步驗證的重要性

兩步驗證從用戶體驗的角度來講確定是不友好的,由於用戶登陸或者操做一個開啓兩步驗證的網站時,用戶須要額外輸入一串隨機密碼以確認用戶是本人操做。對於大多數人來講可能以爲這操做就是多餘的,麻煩的。這麼麻煩不如關掉。安全

我建議對於比較重要的帳號,若是該應用提供了兩步驗證,最好開啓。安全第一。服務器

我舉一下我身邊的一個栗子🌰
大概是2016年,我女友的Apple ID被盜。盜號者解綁了她的郵箱,改用盜號者的郵箱,致使女友iPhone被鎖。盜號者還發郵件勒索500元解鎖,我還加了盜號者qq和他砍價,砍到了200元。最後固然是找蘋果客服解鎖,提供了各類照片、單據和電話確認,歷時2天才解鎖。整體沒什麼損失,就是給本身生活帶來一些不便。看看知乎上被盜號而且盜刷信用卡支付寶的,那才叫慘烈。
若是被盜者開啓了兩步驗證,即便別人有你的Apple ID的帳號密碼,也登陸不了你的帳號。網站

另外,可能不少用戶喜歡在多個網站上使用相同的密碼,這樣是很危險的。若是用戶一個網站上的帳號被盜了,其它平臺的帳號可能也要遭殃。畢竟「某某平臺的帳號系統被盜」這種事也是時有發生的。這種狀況開啓兩步驗證也是能保護其它平臺的帳號不被不法分子利用。this

HOTP 和 TOTP

OTP

兩步驗證中使用的密碼是一次性密碼(One-Time Password 簡稱OTP),也稱爲動態口令。是使用密碼技術實現的在客戶端和服務器之間經過共享密鑰的一種強認證技術,是加強目前靜態口令認證的一種很是方便技術手段,是一種重要的兩步驗證認證技術。Wikipedia解釋spa

HOTP (HMAC-Based One-Time Password Algorithm)

HOTP 是基於 HMAC 算法生成的一次性密碼,也稱事件同步的動態密碼,是 ITEF RFC 4226 公開的算法規範, 僞代碼以下:

 

HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))


客戶端和服務器事先協商好一個密鑰K,用於一次性密碼的生成。客戶端和服務器各有一個事件計數器C,而且事先將計數值同步。Truncate是將HMAC-SHA-1產生的20字節的字符串轉換爲若干位十進制數字的算法。

 

TOTP (Time-Based One-Time Password Algorithm)

TOTP 是 HOTP 的改良版,使用時間替換掉 HOTP 的事件計數器 C,也稱時間同步的動態密碼。詳細規範見 RFC 6238,僞代碼:

 

TOTP = Truncate(HMAC-SHA-1(K,T))


T = (Current Unix time - T0) / X
T0 是初試時間,默認爲 0
X 是時間步長,默認30秒
官方文檔中舉了個栗子,假設當前unix時間=59,T0=0,X=30,則T=1
假設當前unix時間=60,T0=0,X=30,則T=2
也就是對T的值向下取整,拋棄小數的意思

 

實現

瞭解完了規範後,擼代碼就簡單了。

上面的算法除了HMAC-SHA-1以外就是Truncat了,貼一段Truncat的JS代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 將20字節的hmac結果轉爲6位數字,不夠6位前面補0 
truncat(hmac_result) {
const offset = hmac_result[19].charCodeAt() & 0xf;
const bin_code = (hmac_result[offset].charCodeAt() & 0x7f) << 24
| (hmac_result[offset + 1].charCodeAt() & 0xff) << 16
| (hmac_result[offset + 2].charCodeAt() & 0xff) << 8
| (hmac_result[offset + 3].charCodeAt() & 0xff);
let otp = (bin_code % 10 ** this.digit).toString();
while (otp.length < this.digit) {
otp = '0' + otp;
}
return otp;
}

 

看看最終實現效果👇 用你的谷歌驗證器掃一掃試試(在線地址

完整代碼請移步個人Github https://github.com/wuyanxin/totp.js

相關文章
相關標籤/搜索