iT邦幫忙

2026 iThome 鐵人賽

DAY 25
0

為什麼需要「一票否決」的概念

一般的系統驗收是打分數:功能覆蓋 80%、效能達標、可用性 99.9%,加權平均過門檻就通過。

資料防護系統不能這樣驗。因為某些失敗的性質是不可逆且不可補償的——你不能用「其他 95 項都做得很好」來補償「原文外洩了一次」。

所以驗收標準需要兩層:量化指標(打分數)和重大失敗模式(一票否決)。

今天定義後者。

七種重大失敗

F1:原文外洩。

任何情況下,未經去識別化的個資離開了受控邊界。

包含:偵測漏抓導致明文送出、管線異常時 fail-open 放行、OCR 路徑繞過偵測、日誌或錯誤訊息夾帶原文。

判定方式:在測試環境對所有外送流量做全量掃描,任何一筆命中即為 F1。

def check_f1(outbound_payloads, gold_pii_list):
    """外送流量中不得出現任何測試個資的原值"""
    for payload in outbound_payloads:
        for pii in gold_pii_list:
            if pii in payload:
                return Failure("F1", pii_type=classify(pii),
                               payload_id=payload.id)
    return Pass()

F2:靜默失效。

系統在功能異常的情況下回報成功。

包含:掃描 PDF 抽不出文字但回傳成功、偵測器未載入但回傳空 findings、去識別化失敗但輸出原文。

判定方式:注入故障(拿掉規則檔、關閉模型服務、餵損壞檔案),系統必須明確失敗,不得靜默通過。

F3:Vault 明文暴露。

Vault 中的對應關係以明文形式可被讀取。

包含:資料庫未加密、備份未加密、應用服務帳號能直接查詢、日誌記錄了 Token 原值與對應。

F4:越權還原。

不具還原權限的身分成功取得明文。

包含:一般使用者透過 API 直接呼叫還原端點、跨案件存取未被阻擋、憑證未過期檢查、前端隱藏但後端未驗證。

這一項的測試必須從 API 層做,不是從 UI。UI 上把按鈕藏起來完全不算防護。

F5:稽核軌跡缺失。

敏感操作未被記錄,或記錄無法歸屬到具體的人。

包含:還原操作沒有日誌、日誌只記服務帳號、日誌可被業務身分修改或刪除、trace 無法串接。

F6:一致性錯誤導致實體混淆。

同一實體在同一 scope 內得到不同 Token,或不同實體得到相同 Token。

前者造成模型誤判為多人,後者造成誤判為同一人。兩者都會讓法律分析出錯,而且錯得很難發現。

def check_f6(entity_map, deidentified_docs):
    # 一對多:同一實體多個 Token
    for entity, tokens in group_by_entity(entity_map).items():
        if len(set(tokens)) > 1:
            return Failure("F6", kind="split", entity=entity)
    # 多對一:不同實體同一 Token
    for token, entities in group_by_token(entity_map).items():
        if len(set(entities)) > 1:
            return Failure("F6", kind="collision", token=token)
    return Pass()

F7:特種個資被 Token 化而非遮罩。

健康、犯罪、性生活等第 6 條個資被設定為可還原。

這一項是設計層的失敗,不是執行層的。它代表有人在設定去識別化政策時,把不該留還原路徑的欄位留了。

判定方式:檢查 deidentify 設定檔,任何特種個資 infoType 對應到 crypto_replace_ffx_fpe_config 即為 F7。

SENSITIVE_TYPES = {"HEALTH_INFO", "CRIMINAL_RECORD", "GENETIC_INFO"}
REVERSIBLE = {"crypto_replace_ffx_fpe_config",
              "crypto_deterministic_config"}

def check_f7(deidentify_config):
    for t in deidentify_config["transformations"]:
        types = {it["name"] for it in t["info_types"]}
        method = next(iter(t["primitive_transformation"]))
        if types & SENSITIVE_TYPES and method in REVERSIBLE:
            return Failure("F7", types=list(types & SENSITIVE_TYPES))
    return Pass()

這七項怎麼用

在驗收時:任何一項成立,整體不通過。不論其他指標多好。

在開發時:這七項應該是 CI 的 gate。每次 commit 都跑,不過就擋。

在維運時:F1、F2、F5 應該有線上偵測(D20 的告警設計)。

一個常見的爭論:這樣是不是太嚴格

會有人說:「Recall 不可能 100%,你怎麼能要求 F1 零容忍?」

這是兩件事。

Recall 是統計指標,衡量的是偵測能力的平均表現。它確實不可能 100%。

F1 是驗收測試,衡量的是「在這組定義好的測試案例中,有沒有原文外洩」。這組測試案例是有限的、已知的,系統應該全部通過。

如果系統連你設計的測試案例都過不了,那它在真實資料上的表現只會更差。

換句話說:F1 零容忍不是在保證真實世界零外洩,是在確保系統對已知風險有正確行為。 真實世界的殘餘風險,靠 D21 的 fail-closed、D20 的偵測告警、D12 的 Vault 隔離來承接。

把它寫成文件

驗收文件裡的寫法建議:

重大失敗模式(一票否決)

以下情形任一成立者,本次驗收不通過,不因其他項目表現而抵銷:

F1 原文外洩|F2 靜默失效|F3 Vault 明文暴露|F4 越權還原 F5 稽核軌跡缺失|F6 實體混淆|F7 特種個資可還原

每一項應附測試方法、測試案例編號與判定依據。

最後一句很重要。「不通過」必須有客觀依據,不能是主觀判斷。 否則驗收會變成爭論。


明天把這七項變成自動化的回歸測試。


關於作者

我是 Fngi,專注在 AI 資安、LLM 紅隊與 AI 治理框架落地。
IG:@aid3fend

有想討論的架構細節或不同意見,留言或私訊都歡迎。



上一篇
Day 24|攻擊 Vault:不用偷資料庫也能還原
系列文
《30 天為金融法務部門打造 LLM 個資防護閘:去識別化、可控還原與紅隊驗證》 共 25 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言