iT邦幫忙

2026 iThome 鐵人賽

DAY 15
0

大部分架構圖畫錯的地方

回頭看 D2 的資料流:

[2] 格式解析 / OCR  →  [3] PII 偵測  →  [4] 去識別化  →  [5] 送出至 LLM

邊界畫在 [4] 和 [5] 之間。這張圖隱含一個假設:[2] 是在行內做的。

但如果 OCR 用的是雲端服務呢?那實際的資料流變成:

[2] OCR(雲端) ← 原始圖檔在這裡就出行了
      ↓
[3] 偵測(行內)
      ↓
[4] 去識別化(行內)
      ↓
[5] 送出至 LLM

你的整條去識別化管線,保護的是「已經出過行一次」的資料。

而且送出去的是原始圖檔——一張身分證的照片,比任何文字都更完整地暴露個資(照片、簽名、發證日期、戶籍地)。

這個破口在架構圖上完全看不出來,因為 OCR 通常被畫成一個不起眼的前處理方塊。

三個選項

選項一:地端 OCR。

Tesseract、PaddleOCR 都可以本機跑。優點是資料不出行,缺點是中文手寫、印章遮蓋、歪斜掃描的表現明顯不如商用服務。

import pytesseract
from PIL import Image

text = pytesseract.image_to_string(
    Image.open(path),
    lang="chi_tra+eng",
    config="--psm 6",
)

法院文件多半是印刷體,Tesseract 的表現可以接受。客戶手寫的陳情書就很吃力。

選項二:雲端 OCR,但先做圖像層去識別化。

先在地端偵測圖片上的敏感區域、遮蓋,再送雲端 OCR。問題是:要知道哪裡要遮,你得先看懂圖片上寫什麼——這是雞生蛋問題。

實務上只能做粗略處理:對已知版式的證件(身分證、健保卡),用固定座標遮蓋。

# 針對已知版式的證件,用相對座標遮蓋
ID_CARD_REGIONS = {
    "id_number": (0.55, 0.28, 0.95, 0.38),   # 相對座標 (x1,y1,x2,y2)
    "birth_date": (0.55, 0.40, 0.95, 0.50),
    "photo":      (0.05, 0.25, 0.30, 0.75),
}

def mask_known_layout(img, layout):
    w, h = img.size
    draw = ImageDraw.Draw(img)
    for x1, y1, x2, y2 in layout.values():
        draw.rectangle([x1*w, y1*h, x2*w, y2*h], fill="black")
    return img

但這只對版式固定、拍攝角度正的圖片有效。歪斜或裁切過的照片就失準了,而且失準的方式是遮錯位置——個資還在,但你以為遮過了。這比不遮更危險。

選項三:雲端 OCR,但把它納入「行內邊界」。

用 VPC Service Controls 把 Document AI 圈進資料邊界,用 CMEK 控制金鑰,用區域端點確保資料留在指定地區。技術上資料仍然離開了你的機房,但它沒有離開你控制的邊界。

gcloud access-context-manager perimeters update pii_perimeter \
  --add-restricted-services=documentai.googleapis.com \
  --policy=POLICY_ID

這是不是可接受,是一個治理決策而不是技術決策。 它取決於機構怎麼定義「資料不出行」——是物理邊界還是邏輯邊界。這件事必須在架構定案前跟法遵、稽核講清楚,不要留到上線後才發現雙方認知不同。

我的建議:按敏感度分流

跟 D13 的結論一致:

文件類型 OCR 路徑
證件影本(身分證、健保卡、護照) 地端,絕不上雲
含特種個資的文件 地端
法院文書、一般契約 雲端(邊界內)
公開資料、範本 雲端

證件影本那一列沒有商量餘地。一張身分證照片的個資密度太高,而且它的版式固定、印刷體清晰,地端 OCR 完全夠用——沒有理由送出去。

OCR 之後的偵測有什麼不同

OCR 的輸出跟原生文字不一樣,偵測規則要跟著調整。

問題一:辨識錯字。

A123456789  →  Al23456789   (1 誤認成 l)
0912345678  →  O912345678   (0 誤認成 O)

正確答案的個資,OCR 完之後不再符合 regex。這是 D8 說的 Noisy 子集要涵蓋的情境。

處理方式是加入常見混淆對的容錯:

CONFUSION = {"l": "1", "I": "1", "O": "0", "o": "0", "S": "5", "B": "8"}

def normalize_ocr(text: str) -> str:
    """僅在數字上下文中套用,避免破壞正常文字"""
    def fix(m):
        return "".join(CONFUSION.get(c, c) for c in m.group())
    # 只處理「看起來像號碼」的片段
    return re.sub(r"[A-Za-z0-9lIOoSB]{8,12}", fix, text)

要小心這個轉換會製造誤判。它應該用於產生額外的候選,而不是取代原文——原文和轉換後版本都跑一次偵測,取聯集。

問題二:版面順序。

OCR 的輸出順序不一定是閱讀順序。表格會被拆成一行行,欄位標籤跟值可能分開。這會破壞 D7 提到的 hotword 機制——「帳號」和號碼可能相隔很遠。

處理方式是用帶版面資訊的 OCR(Document AI 的 Form Parser 會回傳欄位配對),而不是純文字 OCR。

問題三:置信度。

OCR 會回傳每個字的置信度。低置信度的區域應該標記為「需人工複核」,而不是當成正常文字處理。

def flag_low_confidence(ocr_result, threshold=0.7):
    suspect_regions = []
    for token in ocr_result.tokens:
        if token.confidence < threshold:
            suspect_regions.append({
                "text": token.text,
                "bbox": token.bounding_box,
                "confidence": token.confidence,
            })
    return suspect_regions

因為低置信度區域正是「可能有個資但沒被正確辨識」的地方。

圖像層遮罩:bounding box 的用法

偵測到個資之後,如果要輸出去識別化的圖檔(而不只是文字),需要用 bounding box 在圖上遮蓋。

def redact_image(img_path, findings, out_path):
    img = Image.open(img_path).convert("RGB")
    draw = ImageDraw.Draw(img)
    for f in findings:
        bbox = f["bounding_box"]
        # 外擴幾個像素,避免邊緣殘留
        draw.rectangle(
            [bbox.x1 - 3, bbox.y1 - 3, bbox.x2 + 3, bbox.y2 + 3],
            fill="black",
        )
    # 重新編碼,不要保留原圖層
    img.save(out_path, "PNG")

兩個關鍵:

外擴幾個像素。 OCR 的 bounding box 有時候會切邊,剛好留下最後一個數字的一角。外擴是廉價的保險。

重新編碼、不要用「疊加圖層」的方式遮蓋。 在 PDF 上畫一個黑色矩形,原始文字仍然在文字層裡,複製貼上就能拿到。這是媒體上出過好幾次的經典事故。正確做法是點陣化後重新輸出,或者用真正的 redaction API(PyMuPDF 的 add_redact_annot + apply_redactions)。

# PyMuPDF 的真正 redaction:會實際移除底層內容
page.add_redact_annot(rect, fill=(0, 0, 0))
page.apply_redactions()

最後別忘了 EXIF。手機拍的照片帶 GPS 座標、拍攝時間、裝置識別碼。這些全部是個資,而且完全不在圖像內容裡。

img = Image.open(path)
data = list(img.getdata())
clean = Image.new(img.mode, img.size)
clean.putdata(data)      # 只複製像素,不帶 metadata
clean.save(out_path)

明天談 Token 一致性:同一個人在不同頁、不同文件要拿到同一個 Token。


關於作者

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

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


上一篇
Day 14|多格式分流:一份 PDF 不是一種東西
下一篇
Day 16|Token 一致性:同一個人要對到同一個 Token
系列文
《30 天為金融法務部門打造 LLM 個資防護閘:去識別化、可控還原與紅隊驗證》 共 21 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言