本站每一篇工具指南都以同樣的方式收尾:把你的金鑰放進某個配置裡。這篇講的是金鑰本身。洩露是遲早的事,而不是會不會發生的事,因此 API key 安全最佳實踐的目標並不是永不洩露,而是在出事時把損害控制住。決定結果的是兩個動作:讓每個金鑰都待在你能夠吊銷的邊界之內,並始終保持隨時可輪換的狀態。下文所有內容,要麼在朝這兩個屬性推進,要麼在應對它們被檢驗的那一天。
API key 安全最佳實踐:不只是預防,更要能兜住
大多數團隊記得的一條規則是“不要把金鑰硬編碼”,而這條遠遠不夠。金鑰會從這條規則從未提及的地方漏出去。本地開發工具會把憑據寫進明文檔案:關於要把某個 CLI 的本地憑據檔案當作密碼對待的官方警告並非無的放矢,我們在 Codex 憑據指南裡梳理過那套儲存情況。CI 日誌會帶上 shell 回顯的任何內容。錯誤上報系統不加區分地收集請求上下文。工單、聊天和截圖會收下隊友除錯時隨手貼上的一切。官方生產環境指南的起點和你應有的直覺一致:避免在程式碼或公開倉庫中暴露金鑰,而是通過環境變數或金鑰管理服務把金鑰提供給應用,而不是經由程式碼庫。但請帶著上面那些洩露面再讀下一句,設計目標就一目瞭然:假定其中某一處終有一天會失守,並據此設計,讓這次失守變得平淡無奇。
金鑰洩露的六個地方
對著你自己的系統把這份清單過一遍;每一條都是一項檢查加一個修法。
- 客戶端程式碼與分發的應用。任何交付到使用者手裡的東西,從 Web 前端到桌面端或移動端構建,按定義就是公開的。規則是絕對的:絕不要在客戶端暴露 API key。把模型呼叫經由你自己的服務端邊界轉發;對本地開發工具,則讓使用者自帶憑據。官方指南指向同一個方向:通過環境變數或金鑰管理器把金鑰提供給應用,這本身就預設了“應用”是你掌控的地方。藏在已分發的二進位制裡的金鑰不是配置細節,而是一份公開發布的機密。
- 倉庫與提交歷史。當前檔案是容易查的那部分;真正要緊的是歷史。改一個檔案不能補救任何事,因為舊提交裡仍有那把金鑰,克隆出來的副本也仍然帶著它。凡是曾在倉庫裡出現過的金鑰,哪怕是歷史上的,都要當作已經洩露,並記住只有吊銷才能終結這次暴露。
- CI 與構建產物。列印環境變數塊的流水線日誌、回顯變數的 shell 跟蹤,以及把環境檔案一併打包的構建產物,都在把機密搬進讀者範圍比生產環境更廣的系統。讓金鑰以 CI secret 的方式注入,讓日誌保持掩碼,讓產物裡不含憑據檔案; CI 指南裡的非互動模式適用此處,而同樣的衛生習慣可以推廣到每一條會碰到金鑰的流水線。
- 日誌與錯誤上報。這是最常見的洩露面,因為記日誌本來是一種美德。日誌欄位指南中的規則原樣適用:金鑰與 token 永不進入任何一行日誌,任何看起來像憑據的內容在寫入之前就被過濾掉,並且當過濾器觸發時要發出告警。
- 協作與支援渠道。工單、聊天串、共享文件和截圖,是金鑰得以在最大範圍的讀者面前永生的地方。把政策寫得明確而乏味:憑據永不貼上進任何渠道,任何被貼上到過某處的金鑰都視為已洩露並立即輪換。
- 第三方與公開暴露面。分享到團隊之外的除錯片段、第三方儀表盤,以及任何在你的邊界之外的東西。本文刻意不涉及如何到各處搜尋已暴露的金鑰;要緊的是處置立場。只要暴露有可能性,哪怕尚未確認,就按下面的順序走一遍事件響應,並把該金鑰當作已燒燬。
LLM 應用的金鑰管理:讓洩露變成能扛過去的事
四條設計規則,把遲早會來的洩露從緊急事故變成日常雜務:
- 設定過期時間,並按計劃輪換。官方建議是在建立專案 API key 時設定到期日期,並建立定期輪換金鑰的流程。衛生不是記得去動手,而是一張會動手的日曆。管理員可以在組織或專案層面強制最大生存期,於是新金鑰必須在配置的期限內過期,而不能無限期有效,並且專案層面的限制不得超過組織層面的限制。如果你的平臺提供這項控制,開啟它就把政策變成了物理規律。
- 一把金鑰,一項職責。給每個服務和每個環境各自一把金鑰。當一把金鑰洩露時,爆炸半徑恰好就是用過它的那些應用,而不是你執行的全部。這與許可權範圍裡的最小許可權是同一個直覺,只不過用在憑據本身。
- 優先使用短期憑據,而非長期金鑰。 Anthropic 的 API 文件在其認證頭中展示了這種模式:token
Authorization: Bearer <token>可以是 API key,也可以是通過 Workload Identity Federation 獲取的短期 access token。過期時間以分鐘計的東西,要比活上好幾年的金鑰難以被有效地洩露得多。如果平臺提供短期 token 交換,自動化工作負載應優先採用它。 - 把金鑰留在你掌控的邊界之內。只在服務端,沒有例外。金鑰只應存在於你能吊銷它的地方;一旦落到你夠不著的地方,它就已經丟了。怎麼存、存在哪裡,與發到哪裡同樣重要:使用環境所能提供的最強憑據儲存,而不是最方便的那個檔案,這與企業網路指南在 CLI 工具憑據儲存上所採用的排序邏輯相同。
不停機地輪換 API key
安全地輪換 API key 是一套流程,官方版本寫得很精確:在金鑰過期之前,先建立一個替代金鑰,更新你的應用改用新金鑰,只有在確認替代金鑰可用之後,才吊銷舊金鑰。這個順序是刻意的。先建立替代金鑰,給你一個雙活視窗,在此期間兩把金鑰同時存在,新金鑰已在生產環境中證明自己,之後舊金鑰才退場。相比之下,先吊銷是自己製造的停機,而靠改一個環境檔案碰運氣則是一場等著重啟來引爆的自己製造的事故。如果你曾親手更換過提供商或金鑰,金鑰更換指南裡的操作清單是這套順序的實用配套。
先吊銷已洩露的 API key
當懷疑已經洩露時,順序反過來。官方安全指引說得很直接:如果你認為某把金鑰已被暴露、被濫用或以其他方式失陷,就迅速吊銷它並用新金鑰替換,平臺的安全設定是檢視金鑰並吊銷受影響金鑰的地方。接下來,工程流程如下:
- 先吊銷。先止血,再去理解原因。不要為了收集證據而讓一把可疑的金鑰繼續存活;吊銷是取證視窗的前提,而不是它的犧牲品。
- 替換並部署。建立新金鑰,通過你慣常的金鑰路徑把它部署到位,並驗證應用正常執行。
- 檢查用量與呼叫位置。檢視這把金鑰在何處被使用、從何處發起、用於何事,並與你自己的用量記錄核對。
- 找到洩露面。上面六處中是哪一處造成了這次洩露,什麼檢查本可以更早發現它?把這項檢查加上。
- 寫下來。事件經過、當時的決定、修復方式和新加的檢查,都寫進倉庫文件,好讓下一次輪換繼承這個教訓,而不是重新學一遍。
與上一節的對比本身就是全部要點:計劃內輪換以替換為先,事件響應以吊銷為先。把兩者搞混,團隊不是拖長了失陷,就是製造了一次停機。
還有一條基線:對抗性測試
安全指引還加了一條建議,它應當與金鑰衛生並列,儘管它是一項獨立的控制,而非金鑰保護措施:以對抗性輸入紅隊測試你的應用。既要測一組有代表性的輸入,也要測來自正在主動嘗試攻破產品之人的輸入,包括 prompt injection 的嘗試。在這裡提它的原因是範圍紀律:對抗性測試加固的是應用層,而上面那些做法兜住的是憑據層。兩件事都要做,別把其中一件當成另一件。
金鑰衛生檢查清單
- 你今天就能親手吊銷你持有的每一把金鑰嗎?
- 你能用雙活視窗而不是硬切換來輪換它嗎?
- 每把金鑰都有到期日期,並按計劃輪換嗎?
- 每把金鑰是否只服務一個應用和一個環境?
- 客戶端程式碼裡是否一個平臺金鑰都不帶?
- 洩露過的倉庫歷史記錄,是否已按“補救手段是吊銷而非刪除”的前提被當作失陷處理?
- CI 與構建日誌是否已掩碼,產物中是否不含憑據檔案?
- 日誌與錯誤報告是否對憑據形狀的內容做了過濾,並在過濾器觸發時告警?
- 對工單、聊天和截圖是否有明確的“禁止憑據”規則?
- 你上一次端到端演練輪換(包含吊銷那一步)是什麼時候?
十個問題,兩個屬性:一切可吊銷,一切可輪換。金鑰洩露的那一天,這兩個屬性就是“走個流程”與“一場事故”之間的全部差別。當你的金鑰用於向相容端點認證時,端點層面的細節就寫在平臺自己的文件。