iT邦幫忙

2026 iThome 鐵人賽

DAY 21
0
Security

《30 天為金融法務部門打造 LLM 個資防護閘:去識別化、可控還原與紅隊驗證》系列 第 21 篇

Day 21|fail-closed:那個平常沒人在意、出事時決定一切的設計

  • 分享至 

  • xImage
  •  

一個簡單的問題

偵測引擎逾時了。使用者的文件正等著處理。系統應該:

A. 擋下來,告訴使用者「系統忙碌,請稍後再試」。 B. 放行,把原文送給模型,先讓使用者有東西用。

選 A 是 fail-closed,選 B 是 fail-open。

這個決定在架構文件裡通常佔不到一行,但它決定了系統在最壞情況下的行為。而最壞情況一定會發生。

為什麼 fail-open 這麼容易被選中

沒有人會在設計會議上說「我們選 fail-open 吧」。它是預設發生的,因為它就是「什麼都不做」的結果。

def process(text):
    try:
        findings = detect(text)
        text = deidentify(text, findings)
    except Exception as e:
        log.warning("偵測失敗,繼續處理: %s", e)   # ← 這裡
    return call_llm(text)

這段程式碼看起來很負責任——有 try/except、有 log。但它的行為是:偵測失敗時,把原文送出去。

這種寫法通常來自「不要讓使用者的操作失敗」這個很合理的直覺。在大多數系統裡這是對的。在資料防護管線裡這是災難。

正確的預設

def process(text, ctx):
    try:
        findings = detect(text)
    except DetectionTimeout:
        audit("detection_failed", ctx, {"reason": "timeout"})
        raise ProcessingBlocked("偵測逾時,為保護資料已中止處理")
    except Exception as e:
        audit("detection_failed", ctx, {"reason": type(e).__name__})
        raise ProcessingBlocked("偵測異常,為保護資料已中止處理")

    if not detection_result_is_sane(text, findings):
        audit("detection_suspect", ctx, {"finding_count": len(findings)})
        raise ProcessingBlocked("偵測結果異常,需人工確認")

    return call_llm(deidentify(text, findings))

三個要點:

一、例外一律擋下。 不區分是哪種例外——因為你無法列舉所有可能的失敗方式。

二、記錄失敗。 失敗事件本身是重要的稽核與維運訊號(D20 的健康檢查靠它)。

三、加一個合理性檢查。 這是防止「沒有拋例外但結果錯誤」的情況。

合理性檢查怎麼寫

最危險的失效不是拋例外,是成功回傳了錯誤結果。例如規則檔載入失敗但程式沒報錯,偵測器回傳空清單。

def detection_result_is_sane(text, findings):
    # 一定長度的法務文件,不可能一個個資都沒有
    if len(text) > 500 and len(findings) == 0:
        return False
    # 偵測器數量檢查:確認所有偵測器都有回應
    if len(active_detectors()) < EXPECTED_DETECTOR_COUNT:
        return False
    # 金絲雀:在文件尾端插入已知個資,檢查它有沒有被抓到
    return True

第三項的金絲雀技巧特別有用:

CANARY = "(系統測試:王大明 A199999999 0900-000-000)"

def detect_with_canary(text):
    probe = text + CANARY
    findings = detect(probe)
    canary_hits = [
        f for f in findings if f["start"] >= len(text)
    ]
    if len(canary_hits) < 3:      # 應該抓到姓名、ID、電話
        raise DetectorMalfunction(
            f"金絲雀檢查失敗:預期 3 項,實際 {len(canary_hits)} 項"
        )
    return [f for f in findings if f["start"] < len(text)]

每一次呼叫都驗證偵測器是活的。 成本是多處理幾十個字元,收益是幾乎不可能發生靜默失效。這是我認為整個系列裡 CP 值最高的一個技巧。

fail-closed 的代價要正面處理

擋下來的代價是使用者用不了。如果偵測引擎不穩定,使用者會天天被擋,最後的結果是——有人會去把這個檢查關掉。

所以 fail-closed 必須搭配:

一、高可用性。 偵測服務要有備援、要能水平擴展。它現在是關鍵路徑上的元件,SLA 要對齊主服務。

二、降級路徑,而不是關閉路徑。 提供一個「更嚴格但仍可用」的模式:

DEGRADED_MODE = {
    "use_semantic_layer": False,      # 語意層不可用
    "rule_layer_aggressive": True,    # 規則層放寬,寧可過度遮罩
    "require_human_review": True,     # 結果需人工確認才能送出
    "block_sensitive_doc_types": True # 高敏感文件類型直接擋
}

降級模式的哲學是:能力下降時,保護程度上升。 語意層掛了,就用更激進的規則多遮一些,並且要求人工複核。使用者仍然能工作,只是比較麻煩。

這比 fail-open 好,也比完全擋死好。

三、明確的例外流程。 一定會有「這份文件現在就要送出去,法院明天開庭」的情況。與其讓人繞過系統,不如提供一個需主管核准、全程留痕、事後複查的緊急通道。

def emergency_bypass(doc, ctx, approver, reason):
    require_role(approver, "emergency_approver")
    audit("emergency_bypass", ctx, {
        "approver": approver,
        "reason": reason,
        "doc_hash": sha256(doc),
        "review_required_by": now() + timedelta(days=1),
    })
    notify_security_team(ctx)
    return process_with_manual_review(doc)

有記錄的例外,好過沒記錄的繞過。 如果系統沒有例外通道,使用者會自己找路——用個人帳號、用手機、用別的工具。那時候你連知道都不會知道。

不同環節的 fail 策略不一樣

不是所有環節都該 fail-closed:

環節 失敗時 理由
偵測引擎 擋 漏抓不可逆
去識別化 擋 同上
送出至 LLM 重試後告知失敗 不涉及資料洩漏
還原服務 擋 無法確認授權就不能給明文
稽核日誌寫入 擋 無法留痕的操作不應執行
統計分析 略過 非關鍵路徑

倒數第二列值得展開:如果稽核日誌寫不進去,該不該繼續執行操作?

我的答案是不該。因為一個無法被記錄的還原操作,等於一個不存在的還原操作——出事時你查不到。這叫做 write-ahead audit:先確保能記錄,再執行。

def reidentify_with_audit(req):
    audit_id = audit_log.reserve(req)      # 先預留一筆
    try:
        result = do_reidentify(req)
        audit_log.commit(audit_id, "success")
        return result
    except Exception as e:
        audit_log.commit(audit_id, "failed", error=type(e).__name__)
        raise

如果 reserve() 失敗,整個操作就不執行。

把選擇寫下來

最後一個建議:把 fail 策略明確寫進架構文件,並且讓它成為一個需要簽核的決定。

因為當事故發生、稽核來問「為什麼原文會送出去」,你需要能指著文件說「這是當初的設計決定,由誰在什麼時候核准的」。而不是「因為那段程式碼是這樣寫的」。


明天開始紅隊。第一個目標不是模型,是去識別化引擎本身。


關於作者

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


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

尚未有邦友留言

立即登入留言