9 起安全事件之后,AI 项目的密钥先别只换一把
平台通知来了,最危险的不是没换模型 API Key,而是团队只换了它。把撤销、更新、最小权限和调用核对拆开,才能知道生产权限有没有真的收回来。
10 月里,至少九个 AI 开发平台陆续披露安全事件。这个数字来自 Wiz 的公开汇总,而且它自己也说得很克制:多数事件仍在调查,没有证据表明它们来自同一个漏洞、同一个依赖,甚至不该被轻率地串成同一条攻击链。
但有一个共同点已经足够让工程团队坐直一点:攻击者盯的不是一段模型回答,而是托管环境里那些能直接调用模型、云资源、代码托管和第三方服务的凭据。模型 API Key 看上去像一串用于计费的字符,进了生产环境以后,它更接近一张能刷出真实预算、真实数据和真实权限的门禁卡。
Railway 在 10 月的初步报告里,把两类高风险环境说得很直白:环境变量很多的工作区,以及存放了很多 AI 服务密钥的环境。它们随后与上游服务商协作撤销可能受影响的密钥;同时也明确表示取证还没结束。这个边界很重要:我们可以据此准备处置动作,但不能把仍在调查的细节写成已经盖棺定论的事故故事。

图:Wiz 汇总的 10 月公开披露日期,按时段统计。它说明事件集中出现,不说明它们属于同一次攻击。
真正值得讨论的,不是“又有一家平台出事了”,而是收到一封平台通知之后,团队怎样在不制造二次混乱的前提下,把手里的生产权限收回来。
先分清:你换掉的到底是哪一种权限
很多人的第一反应是:去模型厂商控制台点一下“重新生成 API Key”。这当然要做,但只做这一步,常常只是把最显眼的一张卡收回来了。
一个实际运行的 AI 服务,通常把不同性质的密钥放进同一条部署链路:模型调用、云账号、对象存储、代码托管、监控、支付、邮件或消息通知。它们可能都被同一个预览环境、同一个 CI 任务、同一个部署平台读取。平台侧的一份通知,影响的就不只是某个 OPENAI_API_KEY,而是这整组“谁可以替服务花钱、读数据、发布版本”的授权关系。
所以收到通知时,别先问“哪一把模型 key 泄露了”。先问三个更笨但更有用的问题:
- 这个工作区保存过哪些密钥种类?模型、云、代码托管、支付和消息服务要分开列。
- 哪些密钥被同一个运行环境、构建缓存或预览部署读取过?
- 哪些还是高权限、长期有效、跨环境共用的老凭据?
这不是要把密钥值翻出来贴进表格。相反,盘点表里只记密钥的用途、所属系统、创建位置、最后轮换时间、权限范围和负责人。真正的值留在密钥管理系统或服务商控制台里。把“知道有哪把钥匙”与“看见钥匙内容”分开,本身就是第一层减灾。
先把影响范围画成四条线
密钥事故最常见的返工,不是技术手段不够,而是大家从不同入口各自开始处理:有人在模型厂商后台重置,有人在部署平台里补变量,有人在 CI 配置里搜索,半天后才发现它们其实都指向同一个运行环境。
与其立刻把所有配置翻一遍,不如先给每个受影响工作区画一张很小的关系图。中心是“这次收到通知的平台或项目”,外面只连四类东西:它保存过的凭据、读取这些凭据的运行环境、凭据能到达的上游服务,以及能证明调用是否异常的日志入口。图上不写值,只写名字、用途和责任人。
这张图至少能避免三种误判:
- 以为轮换完成,其实只有控制台里的主 key 换了。 预览部署、旧分支构建和定时任务仍可能在读取旧值。
- 以为同一服务只有一个权限面。 模型调用、对象存储、GitHub、云账号和告警系统的令牌,失效方式与影响范围完全不同。
- 以为账单没有变化就代表没有风险。 有些密钥被验证过但尚未被高频调用;有些异常则先出现在模型、区域、时间段或调用身份的变化里。
盘点的终点也别写成模糊的“已处理”。每一项至少要落到一个明确状态:已撤销、新值已签发、已更新部署、旧值已验证失效、日志已核对。这五个状态未必由同一个人完成,但它们连起来,才叫把权限真正收回来。
第一个半小时:撤销优先,不要先忙着解释
Wiz 在两例企业凭据滥用样本中观察到,供应商披露凭据访问后的 30 分钟内就出现了第一次未授权模型调用。那是两例样本,不是每一家团队的倒计时,但它足够说明一件事:通知抵达后的早期动作,应该以撤销可用性为中心,而不是先开一个长会追问“到底影响了谁”。
这段时间里,顺序可以很朴素:
- 在提供方控制台撤销或禁用疑似受影响的旧凭据,并记下撤销时间。
- 创建替代凭据时,按服务和环境拆开:生产、预发、预览、个人调试不要继续共用一把。
- 把新凭据写回受控的密钥存储,再触发最小范围的部署更新;不要在聊天记录、构建日志或临时文档里传递值。
- 对无法马上轮换的凭据,先收紧允许的模型、区域、调用来源、预算或速率上限,并标记明确的完成时间。
这里有个很容易被忽略的细节:“新 key 已创建”不等于“旧权限已消失”。 旧值可能还躺在某个已经不常看的 preview 环境、遗留定时任务、失败重试队列或本地 .env 里。撤销旧值之后,反而要主动接受一小段失败:报错比后台继续带着旧凭据跑要健康得多。

图:本文的防御性处置顺序。它不替代供应商通知里的具体要求。
接下来的一天:让新凭据少知道一点、少能做一点
轮换完成后,最容易发生的事是团队为了“赶紧恢复服务”,又给新凭据开回原来的大权限。短期看省事,长期看只是把同一扇门换了把新锁。
更稳的做法,是借这次机会把权限拆成几个能独立失效的层次。模型调用密钥不该顺手拥有云资源管理权;部署平台用的令牌不该同时能读取所有生产数据;一个只做内容摘要的后台任务,也不需要调用所有模型、所有区域或无限制地消耗预算。
这类拆分听起来很像安全教科书,落到 Agent 服务反而尤其实际。因为 Agent 往往会跑得更久、重试更多、工具链更长。任何一把高权限密钥一旦跟着它穿过预览、CI、队列和多家服务商,追溯起来都像在搬家后的纸箱里找一颗螺丝。
可以从四个很具体的约束开始:
- 按环境分钥匙。 生产、预发、预览和个人开发分开;这样一次平台事件不会自动扩散到全部环境。
- 按用途分钥匙。 推理、嵌入、文件、部署、通知各用各的,别把“能用”误当成“应当共用”。
- 按边界收权限。 只开业务真的会调用的模型、区域和接口;不需要的跨区域、管理类能力先关掉。
- 按账单设刹车。 给异常调用配预算阈值、频率告警和负责人;它不能阻止所有滥用,但能缩短“发现很晚”的时间。
最小权限不是一句“以后注意”,而是把下一次处置从全线大扫除,变成只换一格电闸。
恢复服务时,别把所有阀门重新拧到最大
事故后的恢复窗口很容易催生另一个坏习惯:为了让报错消失,先把模型范围、区域、并发和预算统统放开,等“稳定了再说”。这会让团队失去一次观察新凭据是否正常工作的机会,也让下次轮换更难拆。
更合适的恢复方式,是先让最小的一条生产路径回来。比如只恢复一个必须的模型或区域,只让一个部署环境读取新值,并把预算告警和错误告警放到同一个负责人能看见的地方。功能恢复后,再按真实业务需要逐步补齐其余权限。慢一点并不等于影响交付;它是在把“服务能跑”与“服务值得放心地跑”分成两次确认。
对于用 Agent 跑长任务的团队,这一步还该看重试和后台队列。旧凭据一旦失效,队列可能会把失败任务成批重放;新凭据刚写入,另一个遗留 worker 又可能继续用旧配置报错。恢复检查里最好明确回答:哪些 worker 已重启、哪些任务被暂停、哪些失败允许重试、哪些必须人工确认后才重新投递。别让“轮换成功”的通知,被一支看不见的后台队列抵消。
随后,给新凭据留一段观察期,而不是关掉告警就宣布结束。验证的是最小权限下的真实调用是否恢复、旧值是否确实被拒绝、异常来源和模型选择是否回到日常范围。能证明这些的,是控制台与审计日志,不是一句“部署绿了”。
如果系统从来没有记录过一把密钥“正常时长什么样”,这次正好补上:它属于哪个服务、平时在什么时段调用、调用哪些模型、单日预算大致落在哪个区间。下次再收到通知,团队不必从零猜测异常,而是能拿一条已知的正常轨迹做对照。
日志别只看账单:先找“身份变了”的调用
API 账单出现尖峰,通常已经是很靠后的信号。更早的线索,往往藏在调用身份和路径的变化里:从未出现过的调用来源、突然冒出的模型或区域、某个低频任务不合常理地持续运行、失败后又不断换参数重试。
Wiz 的两例样本里,攻击者先用少量请求确认凭据可用,之后才枚举模型和配额、扩大调用量。文章里的具体流量规模不能外推到每家企业,但“先验证、摸清边界后再放量”的节奏,值得被转成自己的审计问题:
在这把凭据的正常工作时间、正常模型、正常区域之外,最近 24 小时和 7 天有没有新的调用身份?
这个问题需要由各服务商的日志、账单和审计能力回答,不要把来源 IP、密钥片段或用户内容搬到群聊里“人工排查”。先保留必要的时间窗和事件编号,再由有权限的人在控制台核对。对外部平台通知里写明需要轮换的项,按通知执行;对没有被点名但和受影响环境共存的高权限凭据,按风险分级补查。

图:把异常调用的判断从“账单涨了没”改成可追问的身份、范围和时间窗。
事故通知不该只变成一次换 key 的待办
平台发生安全事件,最讨厌的地方是:它很容易让人误以为风险完全在“平台那边”。可平台只是密钥经过的一个节点。你把哪些权限放进去、这些权限能调用什么、旧值会不会继续在别处存活,还是自己的工程问题。
所以这次清理最好留下一个能复用的小回路:供应商通知到达后,谁负责冻结和轮换;谁更新部署;谁核对上游调用和账单;谁确认旧凭据失效;多长时间内把结果写进一次简短的复盘。它不需要做成一套宏大的流程图,能在下一封通知到来时少开半小时会、少漏一把边缘环境的密钥,就已经很有价值了。
Wiz 的汇总不是让每个团队恐慌性迁移平台的理由,Railway 的初步报告也不等于所有事故的最终解释。它们共同提醒的,是一个更日常的工程事实:把模型接进产品以后,API Key 已经是生产权限的一部分。
你们团队现在最难轮换的那类凭据,卡在发现、授权,还是部署更新?