在 Day 08 完成後端執行環境的部署後,今天的核心任務是建構我們應用的數據持久層。我們將使用 Cloud Firestore,一個 NoSQL、文件導向的資料庫,來儲存使用者及 FIDO 憑證資訊。
在零信任架構中,數據庫本身就是一道關鍵的安全防線。因此,我們的工作將分為兩個主要部分:
這個階段的目標是建立一個不僅結構合理,而且本質安全的數據基礎。
Firestore 以「集合 (Collections)」和「文件 (Documents)」的形式組織數據。基於 Day 06 的設計,我們將實現以下結構:
1. users
集合
此集合用於儲存使用者帳戶的基本資訊。每個文件代表一個獨立的使用者。
/users/{userId}
email
(String): 使用者的唯一識別符,用於登入流程。displayName
(String): 使用者的顯示名稱。createdAt
(Timestamp): 帳戶建立的時間戳。2. credentials
子集合
這是 FIDO 的核心數據儲存區。我們將其設計為 users
文件下的一個子集合,以清晰地表達使用者與其憑證之間的一對多關係。
/users/{userId}/credentials/{credentialId}
publicKey
(String): Base64 編碼後的公鑰字串(CBOR 格式)。這是後端驗證簽章的必要數據。signCount
(Number): 簽章計數器,用於防範重放攻擊,初始值為 0。transports
(Array of Strings): 記錄驗證器的傳輸類型,例如 ["internal"]
或 ["usb"]
。createdAt
(Timestamp): 憑證註冊的時間戳。這個結構將用戶身份資訊與更敏感的加密憑證資訊進行了邏輯隔離,便於我們後續定義更精細的存取權限。
Firestore 安全規則提供強大的伺服器端授權能力。我們的核心原則是預設拒絕 (Default Deny):除非有規則明確允許,否則所有對數據的讀寫操作都將被拒絕。
打開專案根目錄下的 firestore.rules
檔案,並用以下內容覆蓋:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// 規則 1: 保護 users 集合
match /users/{userId} {
// 僅允許已驗證身分的用戶讀取或建立自己的資料文件。
// request.auth.uid 是 Firebase Authentication 提供的用戶唯一 ID。
allow read, create: if request.auth != null && request.auth.uid == userId;
// 禁止用戶端直接更新或刪除用戶文件,這些操作應由後端邏輯控制。
allow update, delete: if false;
}
// 規則 2: 保護 credentials 子集合
match /users/{userId}/credentials/{credentialId} {
// 僅允許用戶為自己建立新的憑證(在 FIDO 註冊流程中)。
allow create: if request.auth != null && request.auth.uid == userId;
// **極其重要**: 明確禁止任何來自用戶端的讀取、更新、刪除操作。
// credential 資料只應由我們的後端服務(使用 Admin SDK)進行讀取與更新。
// 這可以防止用戶端直接查詢到公鑰、簽章計數等敏感資訊。
allow read, update, delete: if false;
}
}
}
規則解釋:
match /users/{userId}
: 此規則應用於 users
集合下的所有文件。
allow read, create
: 我們允許一個經過 Firebase Authentication 驗證的用戶讀取和創建與其 uid
相符的文件。這涵蓋了用戶註冊和讀取自己個人資料的場景。allow update, delete: if false;
: 我們明確禁止了從客戶端直接修改或刪除用戶資料,這確保了數據的一致性與安全性。match /users/{userId}/credentials/{credentialId}
: 此規則應用於 credentials
子集合。
allow create
: 允許用戶在註冊新的 FIDO 驗證器時,創建一筆新的憑證記錄。allow read, update, delete: if false;
: 這是我們安全模型的核心。我們不允許客戶端以任何方式讀取或修改已存在的憑證。例如,在登入驗證時,是我們的後端 Functions 需要讀取 publicKey
和 signCount
,而不是前端應用。後端服務使用 Admin SDK 進行初始化,它將繞過這些安全規則,以管理員權限存取數據。此設計有效防止了不必要的資訊洩漏風險。完成規則編寫後,需要將其部署到雲端使其生效。
部署規則:
在專案根目錄的終端機中,執行以下指令:
firebase deploy --only firestore:rules
成功後,新的安全規則將立即生效。
使用 Rules Playground 進行測試:
在 Firebase Console 中,導航至 Firestore Database
> 規則 (Rules)
標籤頁,即可找到「規則模擬遊樂場」(Rules Playground)。這是驗證規則邏輯的強大工具。
測試案例 1: 用戶讀取自己的資料 (預期: 成功)
get
/users/testUser123
uid
欄位中輸入 testUser123
。測試案例 2: 用戶讀取他人的資料 (預期: 失敗)
get
/users/anotherUser456
uid
欄位仍為 testUser123
。測試案例 3: 用戶讀取自己的憑證 (預期: 失敗)
get
/users/testUser123/credentials/someCredentialId
uid
為 testUser123
。allow read: if false;
規則有效。今天,我們基於 Day 06 的設計,成功在 Firestore 中定義了數據結構,並部署了一套遵循「最小權限原則」的安全規則。這確保了我們的數據庫層本身就是一道堅固的防線,客戶端僅被授予絕對必要的操作權限,而敏感的憑證數據則被保護起來,僅供後端服務存取。
在資料庫層面建立起安全屏障後,我們明天的任務將是為後端程式碼選擇並設定一個 FIDO2/WebAuthn 的伺-服器端函式庫。這將是我們開始處理複雜密碼學驗證的關鍵一步。