iT邦幫忙

2026 iThome 鐵人賽

DAY 19
0

Day 2 列過一張角色矩陣。今天把它變成可執行的權限設計。

角色 上傳 看去識別結果 還原 改政策 看稽核
一般使用者 ✅ ✅ ❌ ❌ ❌
場景管理者 ❌ ✅ ❌ ✅ ❌
授權還原者 ❌ ✅ ✅ ❌ ❌
稽核人員 ❌ ❌ ❌ ❌ ✅

四個角色為什麼這樣切

一般使用者(承辦律師)沒有還原權限,這是最常被質疑的一點。「他自己上傳的文件,為什麼不能看原文?」

因為他本來就有原文——他從文件管理系統拿到的。他不需要「從 Token 還原」這個能力。

需要還原的是模型產出的結果:模型寫了一份摘要,裡面有 Token,律師要把它變成可用的文件。這個動作才需要還原。

而這個動作應該是可控的,因為它是唯一一個「批次產生明文個資」的操作。一份 50 頁的分析報告一次還原,等於一次產出幾百筆明文個資。

場景管理者不能還原。 他能改偵測政策、能決定哪些欄位要保護——這是很大的權力(他可以把某個 infoType 關掉)。正因為權力大,更不能同時給他還原權限。這是職務分離。

稽核人員什麼都不能做,只能看日誌。 而且他看的日誌裡不應該有明文個資——稽核需要知道「誰在什麼時候還原了 3 筆 PERSON 類型的 Token」,不需要知道還原出來是誰。

實作:Cloud IAM 自訂角色

# 授權還原者
title: "PII Reidentifier"
stage: "GA"
includedPermissions:
  - dlp.deidentifyTemplates.get
  - cloudkms.cryptoKeyVersions.useToDecrypt
  - resourcemanager.projects.get

 

# 場景管理者
title: "PII Policy Manager"
stage: "GA"
includedPermissions:
  - dlp.inspectTemplates.create
  - dlp.inspectTemplates.update
  - dlp.deidentifyTemplates.create
  - dlp.deidentifyTemplates.update
  # 注意:沒有 cloudkms 的任何權限

 

# 稽核人員
title: "PII Auditor"
stage: "GA"
includedPermissions:
  - logging.logEntries.list
  - logging.views.access
  # 沒有任何 dlp 或 kms 權限

關鍵在於 cloudkms.cryptoKeyVersions.useToDecrypt 這一項。它是還原能力的唯一來源,只有授權還原者的服務帳號應該有。

加上條件式授權

單純的角色綁定還不夠。應該加上條件,限制時間、來源、範圍:

bindings:
  - role: "projects/PROJECT/roles/piiReidentifier"
    members:
      - "group:legal-authorized-reidentifiers@example.com"
    condition:
      title: "office_hours_and_internal_only"
      description: "僅限上班時間、內網來源"
      expression: |
        request.time.getHours("Asia/Taipei") >= 9 &&
        request.time.getHours("Asia/Taipei") < 18 &&
        request.time.getDayOfWeek("Asia/Taipei") >= 1 &&
        request.time.getDayOfWeek("Asia/Taipei") <= 5

時間限制的價值不在於阻止內部人員(他們上班時間也能做壞事),而在於縮小攻擊窗口。憑證外洩後,攻擊者只有工作日白天能用,而那正是異常行為最容易被發現的時候。

還原請求要帶什麼

權限只是第一關。每一次還原請求都應該是一個有理由的、可歸屬的事件:

@dataclass
class ReidentifyRequest:
    actor: str              # 誰
    scope_id: str           # 哪個案件
    tokens: list[str]       # 哪些 Token(不是「整份文件」)
    reason_code: str        # 理由代碼
    reason_text: str        # 理由說明
    approval_ref: str | None  # 高風險時的核准單號
    trace_id: str           # 串接原始請求

reason_code 用代碼而不是自由文字,是為了讓稽核可以做統計分析:

REASON_CODES = {
    "DRAFT_OUTPUT":   "產出正式文書",
    "CLIENT_CONTACT": "聯絡當事人",
    "COURT_FILING":   "法院遞狀",
    "AUDIT_REVIEW":   "內部查核",
    "OTHER":          "其他(需填說明)",
}

有了代碼,你可以問:「上個月 80% 的還原理由都是 OTHER,為什麼?」自由文字做不到這件事。

逐筆還原,不是整份還原

tokens: list[str] 這個設計是刻意的。還原的單位應該是 Token,不是文件。

因為整份還原意味著:一次操作產生整份文件的所有明文個資。而使用者實際需要的,通常只是其中幾個欄位(例如把摘要裡的當事人姓名填回去)。

def reidentify(req: ReidentifyRequest):
    check_permission(req.actor, "pii:reidentify")
    check_scope_access(req.actor, req.scope_id)

    if len(req.tokens) > BULK_THRESHOLD:
        require_approval(req.approval_ref, req.actor)

    check_rate_limit(req.actor, len(req.tokens))

    results = {}
    for token in req.tokens:
        results[token] = vault.resolve(token, req.scope_id)

    audit_log.write({
        "action": "reidentify",
        "actor": req.actor,
        "scope": req.scope_id,
        "token_count": len(req.tokens),
        "token_hashes": [sha256(t) for t in req.tokens],  # 不是原值
        "reason_code": req.reason_code,
        "reason_text": req.reason_text,
        "trace_id": req.trace_id,
        "ts": now(),
    })
    return results

三個防護:超過門檻要核准(防批次濫用)、速率限制(防慢速大量竊取)、逐筆記錄 Token 雜湊(可事後追查是哪些欄位,但日誌裡沒有明文)。

不可還原欄位的處理

D9 說過,遮罩和概化的欄位不可還原。還原請求碰到這些欄位時,不能報錯,要明確告知:

{
  "reidentified": {
    "TOKEN_PERSON_a7f3": "王小明",
    "TOKEN_PHONE_b21c": "0912-345-678"
  },
  "not_reidentifiable": [
    {"field": "birth_date", "method": "generalization",
     "note": "已概化至年,無法還原至日"},
    {"field": "health_info", "method": "redaction",
     "note": "特種個資已遮蔽,設計上不可還原"}
  ]
}

這個回應格式很重要,因為它讓使用者理解系統的能力邊界,而不是以為系統壞了。而且 note 欄位等於在每一次操作中重申了設計原則。


明天把稽核軌跡做完整。


關於作者

我是 Fngi,專注在 AI 資安、LLM 紅隊與 AI 治理框架落地。
IG:@aid3fend
有想討論的架構細節或不同意見,留言或私訊都歡迎。



上一篇
Day 18|Token 隔離:把爆炸半徑切小
下一篇
Day 20|稽核軌跡:能回答「是誰」的那份紀錄
系列文
《30 天為金融法務部門打造 LLM 個資防護閘:去識別化、可控還原與紅隊驗證》 共 21 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言