別讓模型複誦你的金鑰:日誌脫敏這道閘,兩頭都得有

上個月,我給推理服務做的值班機器人開始大受歡迎:客戶報障,它就拉生產環境日誌的相關行餵給模型,讓模型給出診斷。直到有一天,一份診斷 Markdown 裡原樣出現了一串 32 位元的 token——那是同事啟動時 dump 環境變數列印的 API key,一直躺在日誌裡。

專屬插圖
別讓模型複誦你的金鑰:日誌脫敏這道閘,兩頭都得有

別讓模型複誦你的金鑰:日誌脫敏這道閘,兩頭都得有

上個月,我給推理服務做的值班機器人開始大受歡迎:客戶報障,它就拉生產環境日誌的相關行餵給模型,讓模型給出診斷。直到有一天,一份診斷 Markdown 裡原樣出現了一串 32 位元的 token——那是同事啟動時 dump 環境變數列印的 API key,一直躺在日誌裡。

當時 key 還沒失效,診斷 Markdown 已經發進了對外群組。我在 30 秒內輪換了 key,但那份 Markdown 是明文,還被同步進了內部倉庫,能想到的洩漏管道得挨個清了一遍。

復盤會上最扎心的一點:那條環境 dump 日誌躺在生產日誌裡已經有三週了,之前每次人工排查都直接翻過它,每個人都「看到」了它,但沒有任何一個環節把它當成風險。風險一直存在,只是沒有一道機器閘門攔住它——靠人眼「看到了就繞開」,等於沒有防護。

洞在哪裡

事故前的鏈路有兩個前提,全錯了。

第一,「模型是人类,會動腦筋」:讀日誌時它會自己判斷哪些不該複述。不是的。模型的任務是把上下文裡看到的東西寫進回答,而一串 32 位元的金鑰恰好是它最擅長照抄的格式。我事後專門測過:放一條帶金鑰的日誌,跑五次,五次全文複述,一次不多。

第二,「輸入端清洗就夠了」。我們其實做過一次正則表達式脫敏,但只覆蓋 sk- 前綴和 Bearer header。日誌裡還有一類十六進位 token 和一個內網資料庫 DSN,全沒被攔下。

整改:四層硬閘

後來把脫敏拆成四層,任何一層命中就攔截,沒有人工跳過這個口子:

1. **正則清單(輸入端)**:sk-、AKIA、JWT(eyJ 開頭)、MySQL/Postgres 連線字串、`password=`/`token=` 鍵值對。每發現一種新金鑰,當天補規則,進事故記錄就當天進清單。

2. **高熵啟發(輸入端)**:對長度 ≥ 20 的連續字串段算香農熵,超閾值不直接刪,標出來走人工複核。十六進位 token、base64 編碼的金鑰是正則表達式最容易漏的形態。

3. **輸出回掃(輸出端)**:模型產出的診斷 Markdown 在發出前,再過一遍同一套規則。命中直接攔截,同時警報到值班群組。這一層才是真兜底——上線頭兩週兩次真實攔截(一次金鑰、一次手機號碼)都攔在輸出端,手機號碼嵌在普通句子中間,輸入端根本沒把它當回事。

4. **欄位白名單(源頭端)**:日誌抓取工具不再拉「最近 500 行」,改成只拉指定欄位:時間戳記、層級、服務名稱、錯誤代碼、訊息本體。環境 dump 那一行從此進不了模型的視野。

誤報也交了學費:熵值規則頭兩週誤報了 40 多條,一半是 trace ID 這種天然十六進位。後來加了邊界檢查(前面必須是空白或標點),誤報降到一週一條以內,人工複核的量可以接受了。

怎麼證明閘門真的在工作

管線上線那天,我把事故前的日誌切了一份 fixture(金鑰換成假值)寫成回歸測試用例:以後每次改脫敏規則,先跑一遍 fixture,斷言輸出裡所有原金鑰對應的高熵段都必須被打碼。

這有兩層用意。一是防回歸:規則越長越容易出交互 bug,有一次加 JWT 規則時,`eyJ` 前綴匹配沒相容帶引號的 JSON 日誌,fixture 當場紅掉,才發現。二是信任:後面接這個管道的人不用信我說的「四層都生效」,跑一遍 fixture 自己看結果。

順帶一個成本提醒:回掃是同步跑在出圖路徑上的,用的是本地規則引擎而非外部大型語言模型,單條診斷增加的延遲不到 200 毫秒。如果換成調外部模型審輸出,延遲和成本都會翻好幾倍。

收尾兩句

- 別信模型的「自覺」。只要你希望某類文字「絕對不能出現在輸出裡」,就在管道層做:輸入脫敏 + 輸出回掃,缺一頭,閘門就是擺設。

- 真實洩漏是低機率事件,但它一定會發生,問題只在於管道有沒有「最後一道攔截」。有的話,成本是幾個小時的改造;沒有的話,成本是一次公開事故和一批要輪換的金鑰。

留言區

歡迎分享你的想法!

發表留言

0/500

載入留言中…