在處理個人識別資訊(PII)的開發流程中,「及格線」通常被定義為對直接識別字的徹底屏蔽。然而,真正實作過敏感資料遮罩工具的開發者都知道,這條線遠比想像中難以跨越。當我們試圖自動化這項任務時,我們不只是在編寫程式,更是在建構一套嚴密的資訊架構。
本文將分享從實作一個涵蓋七類識別字(身分證、保單號、姓名、電話、Email、地址、帳號)的遮罩工具中,所獲得的技術洞察。這不僅是關於 Regex 的優化,更是一次關於系統邊界與防禦邏輯的深度反思。
在掃描敏感資料時,正規表示式(Regex)的執行順序決定了分類的精確度。如果缺乏優先級邏輯,系統會陷入「寬樣式收編窄樣式」的災難。
我們必須將「特徵精確度」高的樣式排在前面。例如:
若將帳號樣式放在首位,這組寬鬆的規則會優先「吃掉」原本屬於電話或身分證的數字部分,導致資料屬性歸類錯誤。
> 測試邏輯的核心: 「0912345678 必須歸電話,不得被帳號樣式收編。」
為了確保系統穩定,開發者必須遵循「先窄後寬」原則:先由結構嚴謹的識別字打頭陣,最後才讓寬鬆的長數字串殿後處理。
在開發過程中,最令人印象深刻的失敗案例發生於「冪等性(Idempotency)」測試。為了防止使用者利用全形字元(如全形數字或@)繞過偵測,我們在 Day 6 就建立了一道防禦防線:NFKC 正規化(Normalization),將所有異體字統一轉為標準半形。
諷刺的是,這道防線在 Day 25 卻意外導致了測試失敗。原因在於:第一版的遮罩符號使用了全形星號「*」。
> 斷言失敗代碼片段:
> assert once == twice
E AssertionError: assert '客戶*稱謂已遮罩*…' == '客戶*稱謂已遮罩*…'
當系統執行第二次遮罩時,工具會再次執行 NFKC 正規化,結果把第一次遮罩產生的「全形星號(*)」轉換成了「半形星號(*)」,導致兩次執行結果不一致。
解決方案與架構啟發: 我們最終選擇使用全形括號「【】」作為遮罩符號。這類符號在 NFKC 正規化下具有高度穩定性,不會因為重複掃描而變形。在修正此問題並補強地址樣式的「邊界吞字」缺陷(如:解決地址尾端空格處理不當導致「文化路 10 號」門牌號碼裸奔的 Edge Case Boundary Error)後,系統最終成功通過了全套 212 個測試案例。
「遮完不驗等於沒遮。」作為資訊架構師,我們不能僅依賴正面表列的遮罩邏輯。我們實作了 verify_masked 函式,這是一種程式化的「逆向測試」:在完成遮罩後,系統必須立即進行重掃驗證,且結果必須為「零命中」。
一個成熟的自動化工具必須達成以下三大驗收指標:
開發者往往傾向於宣稱工具無所不能,但真正的專業在於明確定義「工具做不到的事」。我們在設計中提出了一個核心原則:「能力邊界即測試的斷言邊界 (The Capability Boundary is the Assertion Boundary)」。
為了避免給予使用者虛假的安全感,我們刻意將以下內容排除在自動化之外:
承認工具的「反能力」,並將其文件化與測試化,才是負責任的風險管理。
透過打造這套工具,我們從技術面守住了 PII 的「及格線」。然而,技術遮罩終究是事後的補救措施。
身為架構師,我們應該從更高層次思考:與其糾結於如何遮罩已經產生的資料,不如從源頭落實 「資料最小化原則 (Data Minimization)」。與其將整包數據丟給 AI 模型後再來擔心隱私洩漏,不如在傳輸前就盤點每一欄位的必要性。
在追求效能的洪流中,我們應該時常反問自己:「模型真的需要這欄資料嗎?」