iT邦幫忙

2026 iThome 鐵人賽

DAY 24
0

換一個攻擊者視角

前兩天的攻擊目標是「讓個資漏出去」。今天的目標不同:個資已經被正確 Token 化了,攻擊者要把它還原回來。

最直接的方法是偷 Vault。但 D12 已經上了五道防護,直接偷不容易。所以今天談的是不需要直接存取 Vault 的還原路徑。

攻擊一:頻率分析

如果 Token 化是全域確定性的(D16 的層級三),那 Token 的出現頻率會保留原值的分布。

中文姓氏的分布高度不均——陳、林、黃、張、李佔了很大比例。如果攻擊者拿到一批去識別化文件,統計假名的姓氏分布,最高頻的那個假姓氏很可能對應到「陳」。

這不能直接還原個人,但它縮小了搜尋空間。結合其他資訊(例如已知某份文件的當事人是誰),可以逐步建立對應表。

防禦:scoped 一致性(D18)。跨案件不可關聯,就沒有足夠的樣本做頻率分析。單一案件內的樣本量太小,統計不出東西。

攻擊二:已知明文攻擊

攻擊者知道某份文件的原文(例如他自己就是當事人,或他拿得到公開的判決書),也拿得到對應的去識別化版本。

比對兩者,他就得到了一批確定的 (原值, Token) 對應。

如果 Token 化用的是 FPE,這批對應能不能推出金鑰?理論上不行——FF1 的安全性基於底層 AES,已知明文攻擊在計算上不可行。

但如果用的是自己實作的弱方案(例如簡單的查表、或者沒加鹽的 hash),已知明文攻擊可以直接建立字典。

防禦:用標準演算法,不要自己發明。這是密碼學的基本紀律。

另外,D13 那個 HMAC 假名函式要注意:如果 key 洩漏,攻擊者可以離線窮舉所有常見中文姓名,建立完整對應表。所以那把金鑰要跟 FPE 金鑰同等保護。

攻擊三:透過推論結果反推

這個比較有意思。攻擊者不碰 Vault,而是利用系統本身的功能。

假設攻擊者有一般使用者權限(可以上傳文件、可以用 LLM),他可以:

  1. 上傳一份自己構造的文件,裡面放猜測的姓名「王小明」
  2. 觀察系統產生的 Token
  3. 如果 Token 跟目標文件裡的某個 Token 相同 → 確認目標就是王小明

這是一個**確認預言機(confirmation oracle)**攻擊。它不需要還原權限,只需要能觀察 Token。

# 攻擊者的流程
for candidate in common_names:
    doc = f"立約人{candidate},特此聲明。"
    result = upload_and_deidentify(doc, scope_id=TARGET_SCOPE)
    if extract_token(result) == TARGET_TOKEN:
        print(f"確認:{candidate}")
        break

關鍵前提是攻擊者能指定 scope_id。如果他能把自己的文件放進目標案件的 scope,確定性一致就變成了攻擊工具。

防禦:

一、scope 的寫入權限要控制。 使用者不能任意指定 scope_id,必須是他有權限的案件。

def resolve_scope(user, requested_scope):
    if not user_has_case_access(user, requested_scope):
        raise PermissionDenied("無此案件存取權")
    return requested_scope

二、對同一 scope 的高頻上傳告警。 上面那個 for 迴圈會產生大量上傳事件,這是可偵測的。

三、考慮加入每次不同的隨機成分。 但這會破壞一致性(D16),所以不適用於需要跨文件比對的場景。取捨。

攻擊四:時序側信道

Vault 查詢的耗時可能洩漏資訊。

  • Token 存在 → 查到 → 回傳(快)
  • Token 不存在 → 全表掃描或走 fallback → 回傳(慢)

攻擊者透過測量回應時間,可以判斷某個 Token 是否存在於 Vault,進而推論某個實體是否出現在某個案件裡。

防禦:常數時間回應。

import time

MIN_RESPONSE_TIME = 0.05   # 秒

def resolve_with_constant_time(token, scope_id):
    start = time.perf_counter()
    try:
        result = vault.lookup(token, scope_id)
    except NotFound:
        result = None
    elapsed = time.perf_counter() - start
    if elapsed < MIN_RESPONSE_TIME:
        time.sleep(MIN_RESPONSE_TIME - elapsed)
    return result

這個防禦在實務上的優先級不高(要利用它需要大量精確測量),但在高敏感場景值得做。

攻擊五:批次還原濫用

D3 提過這一項,這裡展開。

攻擊者是一個有合法還原權限的內部人員。他不需要繞過任何控制,只需要用他的權限做超出業務需要的還原。

這是最難防的攻擊,因為每一次操作都是合法的。

防禦是偵測而非阻擋:

def check_reidentify_anomaly(actor, token_count):
    baseline = get_user_baseline(actor)   # 過去 30 天的中位數
    
    signals = []
    if token_count > baseline["daily_median"] * 5:
        signals.append("單次量異常")
    if get_today_total(actor) > baseline["daily_p95"] * 2:
        signals.append("單日累計異常")
    if is_outside_business_hours():
        signals.append("非上班時間")
    if get_distinct_scopes_today(actor) > baseline["scope_p95"]:
        signals.append("跨案件數異常")
    
    if len(signals) >= 2:
        alert_security(actor, signals)
        return "REQUIRE_APPROVAL"
    return "ALLOW"

兩個以上訊號才升級,是為了降低誤報。單純加班到晚上八點不該被當成攻擊。

攻擊六:備份與非正式環境

這不是技術攻擊,是流程漏洞,但它是實務上最常見的實際外洩路徑:

  • Vault 的每日備份存在權限較鬆的檔案伺服器
  • 測試環境用正式資料的複本,但沒有同等的防護
  • 開發人員為了 debug,把一份 Vault dump 下載到筆電
  • 離職員工的帳號沒有及時停用

防禦是流程:備份權限與本體同級、測試環境一律用合成資料、禁止 dump 到端點裝置、離職流程納入權限清查。

這些聽起來很基本,但 D12 那五道防護做得再好,都擋不住一份放在共享資料夾的備份。

把攻擊面收斂成檢查清單

紅隊測試時可以照這個順序走:

# 攻擊 需要什麼權限 防禦重點
1 頻率分析 一批去識別文件 scope 隔離
2 已知明文 一組原文+Token 標準密碼學
3 確認預言機 一般使用者 scope 寫入控制
4 時序側信道 能呼叫還原 API 常數時間
5 批次濫用 還原權限 異常偵測
6 備份/測試環境 側門 流程與權限清查

注意第 3 和第 5 只需要合法權限。這是這個架構最真實的風險——不是外部駭客,是內部的過度權限。


明天把「一犯就出局」的失敗模式定義清楚。


關於作者

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

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



上一篇
Day 23|Model Armor:商用護欄擺在哪一層才有用
下一篇
Day 25|重大失敗模式:一犯就出局的那七種
系列文
《30 天為金融法務部門打造 LLM 個資防護閘:去識別化、可控還原與紅隊驗證》 共 25 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言