iT邦幫忙

2025 iThome 鐵人賽

DAY 4
0
Security

『零信任』的革命:從 FIDO 協議到自動化部署,30天打造次世代身分認證系統系列 第 4

【Day 04 —協議詳解 II】信任的誕生:解構 WebAuthn 註冊儀式

  • 分享至 

  • xImage
  •  

前言:從對話到契約

昨天,我們認識了 WebAuthn 世界中的三大主角,並了解了「註冊 (Attestation)」與「驗證 (Assertion)」這兩大核心流程。今天,我們將深入第一階段——註冊。這不僅是簡單的 API 呼叫,而是一場跨越前後端與硬體的信任建立過程,最終目的是簽訂一份嚴密的「密碼學契約」。

密碼學契約的核心構想:
「我,網站的擁有者(信賴方),從此刻起,相信這把獨一無二的公鑰,能夠代表這位使用者。」

讓我們一起拉開序幕,一步步解構註冊流程的每一個細節 🔐

註冊流程的鳥瞰圖:一場精心編排的四幕劇

在深入程式碼之前,先用一張序列圖 (Sequence Diagram) 來鳥瞰整個註冊流程。理解各個角色(使用者、瀏覽器、後端伺服器、驗證器)之間是如何溝通的。

https://ithelp.ithome.com.tw/upload/images/20250818/20151778TUVRQxAqDN.png

流程拆解為八個關鍵步驟:

  1. 請求發起:流程始於前端,它向後端請求一份「註冊選項」。
  2. 後端出題:後端伺服器(RP)產生一份包含隨機挑戰碼 challenge 的選項,回傳給前端。
  3. 呼叫瀏覽器:前端使用 navigator.credentials.create() API,將後端的選項交給瀏覽器處理。
  4. 使用者互動:瀏覽器提示使用者,使用者透過觸摸指紋或插入硬體金鑰等方式與驗證器(Authenticator)互動。
  5. 硬體簽署:驗證器在內部安全地生成一對新的公私鑰,並建立一份「證明 (Attestation)」,然後回傳給瀏覽器。
  6. 回傳後端:瀏覽器將包含新公鑰與證明的憑證,打包送回我們的後端伺服器。
  7. 後端驗證這是最核心的環節。後端必須嚴格驗證這份證明的真實性與完整性,確保它符合挑戰碼、來源網域等所有安全要求。
  8. 完成註冊:驗證通過後,後端儲存公鑰,並告知前端註冊成功。

接下來,一起聚焦在開發者最需要親手打造的環節:第二步的「後端出題」與第七步的「後端驗證」。


第一幕:後端的「出題」- 建構 PublicKeyCredentialCreationOptions

一切始於我們的後端伺服器(RP)產生一份「註冊挑戰書」,也就是 PublicKeyCredentialCreationOptions 物件。這份文件詳細定義了本次註冊儀式的規則與參數,是後端要求瀏覽器和驗證器的配合規則。

PublicKeyCredentialCreationOptions 後端生成範例 (Node.js):

import { randomBytes } from 'crypto';

function generateRegistrationOptions(username, userId) {
  // `RP` (Relying Party) 物件
  const rp = {
    id: 'your-website-domain.com', // **網站的有效域名**
    name: 'FIDO零信任鐵人賽',
  };

  // `user` 物件:代表正在註冊的使用者
  const user = {
    id: userId, // 使用者在系統中的唯一 ID,必須是二進位格式 (ArrayBuffer)
    name: username,
  };

  // `challenge`:一個長度至少為 16 bytes 的隨機、不可預測的字串
  // 這是防止「重放攻擊 (Replay Attack)」的核心機制
  const challenge = randomBytes(32); // 使用加密的隨機數生成器

  // `pubKeyCredParams`:告知驗證器我們接受哪些種類的加密演算法
  // -7 (ES256) 和 -257 (RS256) 是最常見且推薦的選項
  const pubKeyCredParams = [
    { type: 'public-key', alg: -7 },
    { type: 'public-key', alg: -257 },
  ];

  return {
    rp,
    user,
    challenge,
    pubKeyCredParams,
    timeout: 60000, // 允許使用者操作的逾時時間 (毫秒)
    attestation: 'direct', // 證明類型,'direct' 表示從驗證器獲取最直接的證明
  };
}

// 在實際傳送給前端前,二進位的 challenge 和 user.id
// 通常會被轉換成 Base64URL 格式,前端接收後再轉回 ArrayBuffer。

第二幕 & 第三幕:前端傳遞與後端驗證

前端拿到這份 options 後,會呼叫 navigator.credentials.create(),觸發瀏覽器與驗證器的互動。使用者完成生物辨識後,前端會得到一個 PublicKeyCredential 物件,並將其回傳。

現在,好戲登場了。後端伺服器收到了憑證,也就是 AuthenticatorAttestationResponse。這是整個註冊流程中最複雜、但也最關鍵的一步:驗證憑證的真實性

後端驗證 AuthenticatorAttestationResponse 的核心步驟:

這是一個嚴謹的驗證鏈,任何一步失敗,都必須拒絕此次註冊。

  1. 驗證 clientDataJSON:核對基礎資訊

    • 將前端傳來的 clientDataJSON(一個二進位 Buffer)解析為 JSON 物件。
    • 檢查 type:必須是 webauthn.create。確保這是一個註冊請求,而不是登入請求。
    • 檢查 challenge:將 clientDataJSON 中的 challenge (Base64URL 編碼) 與我們當初在 Session 中儲存的 challenge 進行比對。必須完全一致。這是防止跨站請求偽造 (CSRF) 和重放攻擊的關鍵。
    • 檢查 origin:必須與我們網站的來源 (e.g., https://your-website-domain.com) 完全一致。這是 FIDO 協議內建的防釣魚機制,確保憑證是為我們的網站所產生。
  2. 解碼 attestationObject:拆解證明封包

    • attestationObject 是一個採用 CBOR (Concise Binary Object Representation) 格式編碼的二進位資料。我們需要使用專門的函式庫 (如 cbor for Node.js) 將其解碼。
    • 解碼後,我們會得到兩個關鍵部分:
      • fmt:證明的格式 (e.g., "packed", "fido-u2f")。
      • authData:包含了最核心的驗證器數據。
      • attStmt:包含了關於證明本身的敘述。
  3. 驗證 authData:深入驗證器數據

    • authData 也是一個需要按位元組 (byte) 精確解析的二進位結構。
    • 驗證 rpIdHash:取出 authData 的前 32 bytes,這是我們 RP ID (your-website-domain.com) 的 SHA-256 雜湊值。我們必須在後端用同樣的演算法計算一次 RP ID 的雜湊,並確保兩者完全匹配。
    • 檢查 Flags (旗標)authData 的第 33 byte 是一個旗標位元組。
      • 檢查 UP (User Present) 位元是否為 1,確保使用者在場。
      • 檢查 UV (User Verified) 位元是否為 1,確保使用者通過了生物辨識或 PIN 碼驗證。
  4. 提取憑證公鑰並儲存

    • 如果以上所有驗證都通過,我們就可以從 authData 中安全地提取出 credentialId(憑證 ID)和 credentialPublicKey(使用者公鑰)。
    • 將以下資訊與使用者帳戶關聯,並存入資料庫:
      • userId
      • credentialId (未來用於登入時識別是哪個驗證器)
      • publicKey (CBOR 或 PEM 格式)
      • signCount (簽章計數器,初始為 0,登入時會用到)
      • transports (驗證器類型,如 "internal", "usb", "nfc")

第四幕:驗證通過,完成註冊

當後端伺服器走完了上述所有嚴謹的驗證步驟,並成功將使用者的公鑰、憑證 ID 等資訊安全地存入資料庫後,這份「密碼學契約」便正式生效了。

到現在我們正式完成整個註冊流程:

  1. 後端回應:伺服器向前端回傳一個明確的成功狀態(例如,HTTP 200 OK),表示註冊已成功。
  2. 前端反饋:前端接收到成功回應後,會更新使用者介面。一個提示訊息(「裝置註冊成功!」),或是直接將使用者導向到會員後台頁面。

現在新註冊的裝置,就是使用者未來作為驗證登入的「數位鑰匙」。


結語:一份高度安全的信任契約

今天我們一起深入協議層,不僅要瞭解註冊儀式的完整流程,更掌握後端建構 Options 的方法,以及驗證 Response 的每一個關鍵步驟。這個過程雖然複雜,但正是這些環環相扣的驗證,才構建了 FIDO/WebAuthn 堅不可摧的安全性。

現在,使用者與我們的服務之間已經建立了一份基於公鑰密碼學的信任。

明天,我們將探索如何使用這份信任契約,解構 WebAuthn 的第二場核心儀式——驗證 (Authentication),看看登入流程是如何做到比註冊更快速,同時又保持同等級別的安全性的。


上一篇
【Day 03 —協議詳解】WebAuthn 的世界觀:讀懂規格書的第一步
下一篇
【Day 05 協議詳解 III】信任的證明:解構 WebAuthn 驗證儀式
系列文
『零信任』的革命:從 FIDO 協議到自動化部署,30天打造次世代身分認證系統9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言