支援驗證器應用程式驗證你的 Node.js 應用程式
這篇文章介紹如何透過整合驗證器應用程式驗證(如 Google Authenticator 和 Microsoft Authenticator)來提升你的 Node.js 應用程式的安全性。
\n\n在傳統的應用程式中,我們通常使用電子郵件/用戶名/電話作為我們的身份識別符。透過將這些身份識別符與對應的密碼或驗證碼結合,我們可以完成身份驗證過程。\n\n然而,在安全要求更高的情景中,僅依賴身份識別符進行身份驗證是不夠的。這是因為這些身份識別符及其對應的驗證資訊可能容易洩漏。\n\n在這種情況下,需要額外的身份驗證層來確保當前身份識別符正在驗證的實體是真實的使用者。常見的身份驗證方法包括使用驗證器應用程式的 TOTP 驗證、生物識別驗證、裝置驗證等。\n\n本文將探索如何將驗證器應用程式驗證整合到你的 Node.js 應用程式中,增強使用者的身份驗證過程的安全性。\n\n## TOTP 介紹\n\nTOTP 代表基於時間的一次性密碼。正如維基百科所述,它是一種電腦演算法,可生成一次性密碼(OTP),以當前時間作為唯一性的來源。\n\n透過在使用者的手機和應用程式伺服器之間共享 TOTP 秘密金鑰,使用者的手機和應用程式伺服器可以在相同時間生成相同的 TOTP 代碼:\n\nmermaid\nsequenceDiagram\n\tparticipant User Phone\n\tparticipant Shared TOTP Secret\n\tparticipant App Server\n\n\tUser Phone ->> Shared TOTP Secret: 獲取秘密\n\tShared TOTP Secret -->> User Phone: 秘密\n\tUser Phone ->> User Phone: 透過秘密和當前時間生成 TOTP 代碼 (211022)\n\tUser Phone ->> App Server: 發送 TOTP 代碼 (211022)\n\tApp Server ->> Shared TOTP Secret: 獲取秘密\n\tShared TOTP Secret -->> App Server: 秘密\n\tApp Server ->> App Server: 透過秘密和當前時間生成 TOTP 代碼 (211022)\n\tApp Server ->> App Server: 比較生成的代碼與收到的代碼(都為 211022)\n\tApp Server -->> User Phone: 驗證成功\n
\n\n由於 TOTP 生成依賴於時間,它可以在離線情況下計算。此外,TOTP 生成一個數字字串,使其簡單且使用者友好。因此,驗證 TOTP 通常用作二次身份驗證的方法。\n\n當使用者使用 TOTP 作為二次身份驗證方法時,他們經常面臨存儲 TOTP 秘密和生成 TOTP 代碼的挑戰。這時驗證器應用程式派上用場。我們可以使用驗證器應用程式來存儲 TOTP 秘密,驗證器應用程式將自動為你生成 TOTP 代碼。需要驗證時,你只需打開你的驗證器應用程式,就能獲得對應於 TOTP 秘密的 TOTP 代碼。流行的驗證器應用程式包括 Google Authenticator 和 Microsoft Authenticator。\n\n實現 TOTP 作為二次身份驗證涉及兩個步驟:\n\n1. 將 TOTP 秘密綁定到使用者。\n2. 通過相關的 TOTP 秘密驗證使用者的 TOTP 代碼。\n\n將 TOTP 綁定到使用者的過程如下:\n\nmermaid\nsequenceDiagram\n\tparticipant Authenticator App\n\tparticipant User Phone\n\tparticipant App Server\n\n\tUser Phone ->> App Server: (1) 使用身份識別符登入\n\tApp Server ->> App Server: (2) 身份識別符驗證成功,但未從使用者中找到 TOTP 秘密\n\tApp Server ->> App Server: (3) 為使用者生成 TOTP 秘密並在本地儲存\n\tApp Server -->> User Phone: (4) 將 TOTP 秘密發送給使用者\n\tUser Phone ->> Authenticator App: (5) 保存 TOTP 秘密\n\tAuthenticator App -->> User Phone: (6) 由 TOTP 秘密生成 TOTP 代碼\n\tUser Phone ->> App Server: (7) 將 TOTP 代碼發送到伺服器\n\tApp Server ->> App Server: (8) 從使用者的本地 TOTP 秘密中生成 TOTP 代碼\n\tApp Server ->> App Server: (9) 比較收到的 TOTP 代碼與生成的代碼\n\tApp Server ->> App Server: (10) TOTP 代碼驗證成功,將 TOTP 資料存儲在用戶層級\n\tApp Server -->> User Phone: (11) TOTP 驗證成功,登入成功\n
\n\n一旦使用者綁定了 TOTP,他們就可以用它進行驗證。過程如下:\n\nmermaid\nsequenceDiagram\n\tparticipant Authenticator App\n\tparticipant User Phone\n\tparticipant App Server\n\n\tUser Phone ->> App Server: (1) 使用身份識別符登入\n\tApp Server ->> App Server: (2) 身份識別符驗證成功,找到了使用者的 TOTP 秘密\n\tApp Server -->> User Phone: (3) 要求使用者進行 TOTP 驗證\n\tUser Phone ->> Authenticator App: (4) 打開驗證器應用程式\n\tAuthenticator App -->> User Phone: (5) 通過使用者的 TOTP 秘密生成 TOTP 代碼\n\tUser Phone ->> App Server: (6) 將 TOTP 代碼發送到伺服器\n\tApp Server ->> App Server: (7) 獲取已驗證的使用者 TOTP 秘密並生成 TOTP 代碼\n\tApp Server ->> App Server: (8) 比較收到的 TOTP 代碼與生成的代碼\n\tApp Server ->> App Server: (9) TOTP 代碼驗證成功\n\tApp Server -->> User Phone: (10) 登入成功\n
\n\n如圖所示,在使用者端,我們使用驗證器應用程式來管理 TOTP 秘密並生成 TOTP 代碼。在伺服器端,我們需要支持生成 TOTP 秘密並驗證使用者傳來的 TOTP 代碼。本文將使用 otpllib 作為範例來整合伺服器端的 TOTP 相關功能。\n\n## 支持你的 Node.js 應用程式中的 TOTP\n\n假設你的應用程式基於 Express.js,用戶透過 /sign-in
端點登入,支援使用者登入過程中的 TOTP 的計畫如下:\n\n1. 當使用者未綁定 TOTP 時,以 QR 碼的形式將 TOTP 秘密發送給使用者並提示他們綁定 TOTP。\n2. 當使用者已綁定 TOTP 時,提示他們驗證 TOTP。\n\n首先,我們來安裝專案的依賴項:otplib
和 qrcode
:\n\nbash\nnpm install otplib qrcode --save\n
\n\n接下來,讓我們加強 /sign-in
端點。\n\njs\nimport qrcode from 'qrcode';\nimport { authenticator } from 'otplib';\n\n// 其他程式碼...\n\napp.post('/sign-in', async (req, res, next) => {\n const { username, password } = req.body;\n const { totpSecret, userId } = await verifyIdentity({ username, password });\n\n // 緩存識別符驗證狀態\n req.inMemoSession.set('userId', userId);\n\n // 檢查是否存在 TOTP 秘密\n if (!totpSecret) {\n // 綁定 TOTP\n const secret = authenticator.generateSecret();\n const keyUri = authenticator.keyuri(username, 'DemoApp', secret);\n const secretQrCode = await qrcode.toDataURL(keyUri);\n\n // 緩存使用者待綁定 TOTP 秘密\n req.inMemoSession.set('pendingTotpSecret', secret);\n\n res.status(403).json({ error: 'missing_totp', secretQrCode });\n next();\n return;\n } else {\n // 需要 TOTP 驗證\n res.status(403).json({ error: 'totp_verification_required' });\n }\n // ...\n});\n
\n\n根據我們的實作,當使用者未綁定 TOTP 時,我們會將 QR 碼發送到前端:\n\n\n\n使用者使用驗證器應用程式掃描 QR 碼後,驗證器應用程式將生成 TOTP 代碼並存儲相關的 TOTP 秘密。\n\n\n\n使用者將獲得的 TOTP 代碼發送回應用程式伺服器。如果代碼被成功驗證,我們便可以將該 TOTP 綁定到使用者。\n\n所以,讓我們實作一個 /verify-totp
API 來接收使用者發送的 TOTP 代碼:\n\njs\napp.post('/verify-totp', async (req, res, next) => {\n const { totpCode } = req.body;\n const userId = req.inMemoSession.get('userId');\n\n // 檢查待綁定的 TOTP 秘密\n const pendingTotpSecret = req.inMemoSession.get('pendingTotpSecret');\n const totpVerified = authenticator.check(pendingTotpSecret, totpCode);\n if (!totpVerified) {\n res.status(400).json({ error: 'invalid TOTP code' });\n next();\n return;\n }\n\n // 驗證成功後,開始綁定 TOTP\n const user = DB.findUserById(userId);\n await user.update({ totpSecret: pendingTotpSecret });\n\n // 處理使用者登入\n // ...\n});\n
\n\n這樣,我們就成功地為使用者綁定了 TOTP。隨後,當使用者登入時,他們只需打開驗證器應用程式,發送對應於先前綁定的 TOTP 秘密的驗證碼,身份驗證過程將完成。\n\n\n\n在 /verify-totp
API 中,我們使用先前綁定到使用者的 TOTP 秘密來驗證 TOTP 代碼。\n\njs\napp.post('/verify-totp', async (req, res, next) => {\n const { totpCode } = req.body;\n const userId = req.inMemoSession.get('userId');\n\n // TOTP 綁定...\n // 代碼...\n\n // TOTP 驗證\n const user = DB.findUserById(userId);\n const { totpSecret } = user;\n\n if (!totpSecret) {\n // 處理錯誤\n // ...\n next();\n return;\n }\n\n const verified = authenticator.check(totpSecret, totpCode);\n if (!verified) {\n res.status(400).json({ error: 'invalid TOTP code' });\n next();\n return;\n }\n\n // 驗證成功\n // 處理使用者登入\n // ...\n});\n
\n\n## 總結\n\n基於此文章,你應該現在能夠為你的應用程式整合驗證器應用程式驗證。然而,這只是個簡單的例子。當你的應用程式規模較大或預計會變得複雜時,整合新身份驗證方法可能會引起相當大的成本。\n\n好消息是:Logto 作為全面的身份驗證解決方案提供商,提供多因素身份驗證(MFA)支持,包括驗證器應用程式驗證。借助 Logto,你可以在幾分鐘內將安全且高效的 MFA 使用者登入過程無縫整合到你的應用程式中!\n\n