這週我碰到三次金鑰外洩的狀況。三次都不是「不小心貼到公開的地方」這種老梗,而是從我自以為安全的環節漏出去:打包、遮蔽指令、build 設定。這篇只講機制,不貼任何值。
入口一:打包
我的 harness 裡有一個 skill,另外做了一份給網頁上傳用的版本。這份網頁上傳版把 TikHub 和 Apify 的金鑰內嵌進去了,而且已經經過兩次 commit 推上私有 remote。
我原本以為有備份的金鑰掃描擋著,結果掃描規則抓不到 apify_api_ 這個前綴。也就是說,掃描一路綠燈,不代表裡面沒有金鑰,只代表它認得的格式裡沒有。這條規則我已經補上了。
私有 remote 不等於沒有外洩,推上去就是推上去了。
入口二:遮蔽指令本身
處理上面那件事的時候,遮蔽用的是 sed,但 macOS 的 BSD sed 不支援 \s。
於是遮蔽的 pattern 沒有比對到,替換沒有發生,完整的值就直接印進對話了。工具沒有報錯,輸出看起來也很正常,只是該被遮掉的東西沒被遮掉。
入口三:build 設定
第三個是我自己產品的前端專案。vite.config.ts 裡有一段 define,會把本機 .env 的 GEMINI_API_KEY 直接燒進前端 bundle。
CI 上沒有這個環境變數,所以 CI 打出來的線上版本是乾淨的。只有在本機 build、再從本機部署的時候,key 才會跟著 bundle 一起出去。
我已經開了待辦去修設定,在修好之前的規則是:手動部署一律先把那個變數清空。
兩天後查證:那把 key 早就失效,過去六週沒有實質用量,現在的 bundle 也不再帶 key。
輪換還是只提醒
三件事處理完,我回頭看自己判斷要不要輪換金鑰的準則。我的做法是先查再決定,不憑感覺:
- 先跑
git log --all -S "<前綴>",再看git remote -v。 - 如果只存在本機,或只出現在這次對話裡,沒進 git 歷史、也沒推上 remote,就只提醒、清掉紀錄,不輪換。
- 如果已經推上 remote,包含 GitHub 私有庫,就視同外洩,必須輪換。刪歷史救不回已經被 clone 或快取的值。
- 回報曝險的時候一律遮蔽,只露前 6 碼加末 2 碼,不貼完整金鑰。