本站每一篇工具指南都以同样的方式收尾:把你的密钥放进某个配置里。这篇讲的是密钥本身。泄露是迟早的事,而不是会不会发生的事,因此 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 与构建日志是否已掩码,产物中是否不含凭据文件?
- 日志与错误报告是否对凭据形状的内容做了过滤,并在过滤器触发时告警?
- 对工单、聊天和截图是否有明确的“禁止凭据”规则?
- 你上一次端到端演练轮换(包含吊销那一步)是什么时候?
十个问题,两个属性:一切可吊销,一切可轮换。密钥泄露的那一天,这两个属性就是“走个流程”与“一场事故”之间的全部差别。当你的密钥用于向兼容端点认证时,端点层面的细节就写在平台自己的文档。