js 實作 2FA 驗證


Posted by david-christian on 2021-12-14

在介紹如何透過 javascript 實作 2FA驗證流程時,我們要先了解 2FA 是甚麼?

什麼是2FA?

雙重身份驗證(Two Factor Authentication, 2FA) 是一款動態密碼驗證流程。網站與使用者約定一組金鑰,透過 QRcode 或是其他方式,綁定產生驗證碼工具後,在進行相應的操作時,使用者必須輸入產生驗證碼工具上動態生成的驗證碼,校驗通過後才能通過驗證。

什麼是產生驗證碼工具?

Google Authenticator、microsoft authenticator,都是產生驗證碼的工具,透過綁定金鑰,可以定時生成基於該金鑰的一次性驗證碼。

驗證碼是如何生成的?

網站與使用者約定一組金鑰後,透過 TOTP公開標準,以每一次生成驗證碼時(通常以30秒作為一個單位),當下的時間做為參數,來運算生成一次性的驗證碼。

TOTP 標準

TOTP是一個公開標準,基於約定的私鑰加上當下的時間,來生成一次性的密碼,也就是說這樣的驗證機制達到去除中心化,你不需要依靠任何第三方機構來達成驗證,雙方基於私鑰及當下時間來達成信任,而透過這樣的方式,一次性密碼也會有有效性,時間一到,該密碼就失效了,產生驗證碼工具又會生成下一個一次性密碼。

javascript 實作 2FA 驗證機制

npm install speakeasy

這邊透過 speakeasy 這個 npm 套件來實作
當然還有許多套件,如 node-2fa 等等

server 端


const speakeasy = require('speakeasy');

引入模塊


const secret = speakeasy.generateSecret();

console.log(secret)
// 返回 {
  ascii: 'p3SeU(m>9ww#%4q1e1ptE7BI7V0f:s52',
  hex: '7033536555286d3e39777723253471316531707445374249375630663a733532',
  base32: 'OAZVGZKVFBWT4OLXO4RSKNDRGFSTC4DUIU3UESJXKYYGMOTTGUZA',
  otpauth_url: 'otpauth://totp/SecretKey?secret=OAZVGZKVFBWT4OLXO4RSKNDRGFSTC4DUIU3UESJXKYYGMOTTGUZA'
}

使用 generateSecret() 該方法可以生成金鑰,會返回 ascii 和 hex 以及 base32 ,三種編碼的金鑰,可以選擇一個來使用即可,而 otpauth_url 則可以透過 qrcode 相關的套件來生成 qrcode ,產生驗證碼工具掃描後,即可綁定該金鑰。


// 假設我們收到的一次性驗證碼為 '132890'
const userToken = '132890'

// 我們選用 base32編碼的金鑰
const key = secret.base32

const verified = speakeasy.totp.verify({ secret: key,
                                       encoding: 'base32',
                                       token: userToken });
console.log(verified)
// 返回 true

totp.verify() 可以驗證金鑰與驗證碼是否正確,並返回布林值
要特別注意 encoding 需使用與金鑰對應的編碼。


const verified = speakeasy.totp.verify({ secret: key,
                                       encoding: 'base32',
                                       token: userToken, 
                                                                             window: 2 });

window 可以基於驗證當下的時間戳,並以驗證碼時效為單位(預設30秒),以 2 為例,則是該驗證當下的時間戳的前後 30 x 2 = 60秒,並判定一次性驗證碼的時效性是否在範圍內。
簡單來說,透過設定 window 參數,你可以拉長一次性驗證碼的有效時間。


client 端


const token = speakeasy.totp({
  secret: key // 金鑰
  encoding: 'base32'
});

通常是直接透過產生驗證碼工具來讓使用者生成一次性驗證碼
但一樣可以透過程式碼來生成一次性驗證碼,而生成的方式就像開頭所介紹的,無論是驗證亦或是產生,雙方都是基於金鑰去達到目的的。

js 實作 2FA驗證的介紹就到這邊,其他更詳細的參數以及更多用途的方法,可以參考 speakeasy 的 github,有幫助到你妳也請給我一點鼓勵,如果有誤的地方,再請大大們教導,感謝你妳的閱讀

本文參考:https://github.com/speakeasyjs/speakeasy


#2FA







Related Posts

Day 166

Day 166

Create-react-app 錯誤訊息

Create-react-app 錯誤訊息

W17_後端框架 Express + Sequelize

W17_後端框架 Express + Sequelize


Comments