Google Cloud 的 Sensitive Data Protection(原名 Cloud DLP)內建了大量 infoType 偵測器。跟台灣場景相關的有:
CREDIT_CARD_NUMBER(含 Luhn 驗證)EMAIL_ADDRESS
PHONE_NUMBER
IBAN_CODE、SWIFT_CODE
PERSON_NAME、LOCATION、DATE_OF_BIRTH
TAIWAN_ID_NUMBER(有,但建議自己驗證涵蓋範圍)實務上會發現,內建的 PERSON_NAME 對中文姓名的表現不如對英文姓名。這不意外——它的訓練分布如此。所以中文情境幾乎一定需要自訂偵測器。
一、正規表示式偵測器(Regex)
{
"customInfoTypes": [
{
"infoType": { "name": "TW_HEALTH_CARD" },
"regex": { "pattern": "\\b\\d{12}\\b" },
"likelihood": "POSSIBLE"
}
]
}
注意 likelihood 設成 POSSIBLE 而不是 VERY_LIKELY。因為「12 位數字」這個 pattern 本身很弱,設太高會淹沒在誤判裡。
二、字典偵測器(Dictionary)
適合有限集合,例如行政區名、常見姓氏、疾病名稱清單。
{
"infoType": { "name": "TW_DISTRICT" },
"dictionary": {
"wordList": { "words": ["中山區", "大安區", "信義區"] }
}
}
大型字典可以放 Cloud Storage,用 cloudStoragePath 指向。
三、熱詞規則(Hotword Rules)
這是我覺得最有價值、也最少人用的功能。它可以根據上下文調整信心度。
{
"infoType": { "name": "TW_BANK_ACCOUNT" },
"regex": { "pattern": "\\b\\d{10,14}\\b" },
"likelihood": "UNLIKELY",
"detectionRules": [
{
"hotwordRule": {
"hotwordRegex": { "pattern": "帳號|帳戶|存款帳號|匯入帳號" },
"proximity": { "windowBefore": 20, "windowAfter": 5 },
"likelihoodAdjustment": { "fixedLikelihood": "VERY_LIKELY" }
}
}
]
}
這解決了 D5 提到的「格式相同但語意不同」問題的一部分:一串 10–14 位數字,如果前面 20 字元內出現「帳號」,那它很可能真的是帳號;否則就是一般數字。
這是規則式方案能做到的、最接近上下文理解的程度。
from google.cloud import dlp_v2
dlp = dlp_v2.DlpServiceClient()
parent = "projects/YOUR_PROJECT/locations/asia-east1"
inspect_config = {
"info_types": [
{"name": "CREDIT_CARD_NUMBER"},
{"name": "EMAIL_ADDRESS"},
{"name": "PHONE_NUMBER"},
],
"custom_info_types": [
{
"info_type": {"name": "TW_ID"},
"regex": {"pattern": r"[A-Za-z][12]\d{8}"},
"likelihood": "VERY_LIKELY",
},
{
"info_type": {"name": "TW_BANK_ACCOUNT"},
"regex": {"pattern": r"\b\d{10,14}\b"},
"likelihood": "UNLIKELY",
"detection_rules": [{
"hotword_rule": {
"hotword_regex": {"pattern": "帳號|帳戶|匯入"},
"proximity": {"window_before": 20},
"likelihood_adjustment": {"fixed_likelihood": "VERY_LIKELY"},
}
}],
},
],
"min_likelihood": "POSSIBLE",
"include_quote": False, # 重要:見下方說明
"limits": {"max_findings_per_request": 3000},
}
response = dlp.inspect_content(
request={
"parent": parent,
"inspect_config": inspect_config,
"item": {"value": text},
}
)
include_quote 一定要設 False。
這個參數控制回應裡要不要包含被偵測到的原文片段。設成 True 的話,你的偵測結果本身就變成一份個資清單——而這份回應可能會被寫進應用日誌。日誌變成第二個個資倉庫,這是 D3 威脅模型裡列過的項目。
要定位就用 byteRange 和 codepointRange,不要用 quote。
location 要指定亞洲區域。
parent 裡的 location 決定資料在哪裡被處理。用 global 的話你不知道它跑到哪去了。對金融場景,這一項在合規審查會被直接問到。可用的亞洲區域包含 asia-east1(台灣)、asia-northeast1(東京)等。
SDP 回傳的 byteRange 是 UTF-8 位元組位置。中文字在 UTF-8 是 3 個位元組,所以位元組位置跟 Python 的字元位置不一致。
def byte_range_to_char_range(text: str, byte_start: int, byte_end: int):
b = text.encode("utf-8")
char_start = len(b[:byte_start].decode("utf-8", errors="ignore"))
char_end = len(b[:byte_end].decode("utf-8", errors="ignore"))
return char_start, char_end
或者直接用 codepointRange,如果 API 版本有提供的話。這個坑會造成遮罩偏移,而且偏移量隨中文字數變動,debug 起來很花時間。
用 SDP 有一個必須面對的問題:要做偵測,就要把原文送給它。
也就是說,資料在「被去識別化之前」就已經離開了行內。這對某些機構是可接受的(SDP 是 Google Cloud 服務,走 VPC Service Controls 可以限制邊界),對某些機構完全不行。
| 考量 | 雲端 SDP | 地端自建 |
|---|---|---|
| 中文表現 | 需大量自訂 | 可針對性訓練 |
| 偵測前資料出行 | 是 | 否 |
| 維運成本 | 低 | 高 |
| 特種個資(敘述性) | 弱 | 可訓練 |
| 稽核可解釋性 | 中 | 高(自己的規則) |
我的實務建議是混合:規則層用雲端 SDP(規則本身不敏感,且 SDP 的 hotword 機制很好用),語意層用地端模型。 但這樣仍然有偵測前出行的問題,所以更保守的作法是規則層也放地端,SDP 只用在去識別化與 Token 化(D11)——那時候送過去的已經是待處理的資料,且可以用 CMEK 控制金鑰。
明天談評測。因為以上所有東西,你都需要能證明它有效。
我是 Fngi,專注在 AI 資安、LLM 紅隊與 AI 治理框架落地。
有想討論的架構細節或不同意見,留言或私訊都歡迎。