API 密钥被盗与蒸馏攻击:如何保护你的 AI 工具密钥

指南John Silver29 分钟阅读

AI 提供商的 API 密钥是一种持有者凭据:谁拿着它,谁就能花你的钱、用你的配额,而提供商无法把持有者和你区分开。蒸馏攻击是有人想要大量这类密钥的原因之一。它用更强模型的回答去训练一个更便宜的模型,需要数量极大的付费请求。如果这些请求走的是被盗的密钥,付钱的就是别人,流量也不像来自单个客户。实用的防御办法是:限制每个密钥能做的事,监控它在做什么,并把密钥存放在尽可能少的地方。

2026 年 10 月 17 日,OpenCode 通报了一起安全事件,并将其与一次大规模蒸馏攻击联系起来。本文从 OpenCode 所说的内容出发,解释什么是蒸馏以及为什么密钥成为目标,列出滥用的迹象,并为使用 AI 工具的人和构建 AI 工具的人提供检查清单。文中也说明 zn-gate 对这个问题能做什么、不能做什么。

OpenCode 说发生了什么?

我们只转述 OpenCode 公开说过的内容。OpenCode 在 X 上的帖子及其发给受影响用户的通知,按 Officechai 和 Blockonomi 的报道引述如下,并附有链接。OpenCode 没有公开说过的内容,一律标为未经证实。

2026 年 10 月 17 日,OpenCode 在 X 上写道:"We had a security breach last night related to a large scale distillation attack that impacted 1.4% of OpenCode Console users"(中文:昨晚我们发生了一起安全事件,与一次大规模蒸馏攻击有关,影响了 1.4% 的 OpenCode Console 用户)。据 Blockonomi 引述,同一帖子称受影响的用户已在当天上午收到电子邮件通知,并且 "if you did not get an email you were not impacted"(如果你没有收到邮件,你没有受到影响)。OpenCode 还补充说 "it was a subset of users who had external providers"(受影响的是配置了外部提供商的那部分用户)。

据 Officechai 引述,发给受影响用户的通知称 OpenCode "identified unauthorized access to a legacy system"(发现有人未经授权访问了一个旧系统),并且 "third party provider keys (eg OpenAI, Anthropic) saved in your workspace may have been exposed"(保存在你的 workspace 中的第三方提供商密钥,例如 OpenAI、Anthropic,可能已被暴露)。通知还说 "the incident is ongoing so we cannot share more details at this point"(事件仍在进行中,因此目前无法分享更多细节)。

以上就是已确认的全部情况。其中有些结论可以推出,有些不能:

  • 1.4% 是 OpenCode Console 用户中的比例,不是所有使用开源 OpenCode 编程智能体的人中的比例。OpenCode 文档把 Console 描述为你添加账单信息和额度、复制 API 密钥的地方,并说明 workspace 的所有者和管理员可以连接自己的提供商。
  • 根据通知,面临风险的是保存在 workspace 中的第三方提供商密钥。
  • OpenCode 没有公布攻击者如何进入、拥有多长时间的访问权限、有多少密钥被使用,也没有公布调用了哪些模型。你在其他地方读到的关于这些问题的内容都只是假设。

我们每天都在使用 OpenCode,既用官方发布的版本,也用我们按自身配置调整的构建,并且信任这个产品。我们与 OpenCode 团队没有任何商业关系。我们写这起事件,是因为它展示了每个 AI 工具厂商和每个 AI 工具用户都应当预先考虑的模式,也因为在调查仍在进行时,该团队保持了公开透明。我们所知不超过他们所说,也不对他们的系统做猜测。

什么是蒸馏攻击,什么是模型提取?

用 Anthropic 关于蒸馏攻击的文章的话说,蒸馏就是 "training a less capable model on the outputs of a stronger one"(用更强模型的输出训练一个能力较弱的模型)。这项技术合法且由来已久。Hinton、Vinyals 和 Dean 在 2015 年的 Distilling the Knowledge in a Neural Network 中把它描述为把一组模型压缩成一个更小、运行成本更低的模型的方法。提供商会蒸馏自己的模型,为客户提供更小、更便宜的版本。

滥用是指未经许可蒸馏别人的模型。Anthropic 的文章说,竞争对手借此以 "in a fraction of the time, and at a fraction of the cost"(只需很小一部分时间和成本)获得能力,而且这样得到的模型很可能不会保留原有的安全防护。文中 Anthropic 报告了三场行动,合计通过约 24,000 个欺诈账户与 Claude 产生了超过 1600 万次交互,并说非法行动是靠模式识别出来的:"massive volume concentrated in a few areas, highly repetitive structures"(体量巨大、集中在少数领域、结构高度重复)。

一个相关概念是模型提取。在 Stealing Machine Learning Models via Prediction APIs(USENIX Security 2016)中,Tramèr 等人表明,只能查询模型的人就能高保真地复制它的行为,而且去掉输出中的置信度值并不能完全阻止这一点。该论文讨论的是经典模型,而不是大语言模型,但结论依然适用:只要能对模型查询足够多次,就能从它的回答中复制它的行为。

这两种技术都不需要访问权重。它们需要的是规模。密钥就是在这里派上用场的。

为什么攻击者要用被盗的 API 密钥来做这件事?

有两个原因,都与成本和可见性有关。

受害者付费。 一次蒸馏需要大量请求,提供商会对每一个请求计费。用被盗的密钥,账单记在密钥主人头上。攻击者的成本降低为收集密钥的功夫。

痕迹被分散。 如果提供商看到一个账户发出数百万条重复的请求,就能发现它,正如 Anthropic 所描述的,靠分类器、行为指纹以及能发现多个账户之间协同活动的工具。如果同样的流量被分摊到来自不同客户的数百个密钥上,每个密钥只发送少量看起来很普通的流量。提供商眼中任何单个账户都显得平常,只有把许多账户放在一起看,模式才会浮现。提供商一侧的检测因此很困难,而密钥的主人可能是第一个能看出异常的人,因为只有他知道自己的正常用量是多少。

结果是,密钥的主人有一个别人都没有的理由去盯着它。

被盗的密钥从哪里来?

密钥是从存放它们的地方泄露的。实际中常见的有这些:

地点 密钥如何离开
.env 文件和 shell 配置文件 被误提交到代码仓库,被脚本、备份或依赖读取,或被编程智能体读取后再发送给它的模型提供商
工具和编辑器配置 密钥被粘贴进会同步、共享或附在错误报告里的设置文件
CI 日志和构建产物 密钥被调试输出打印出来,或被复制进别人可以下载的产物
聊天、工单和文档 为了求助而粘贴出来的密钥,之后一直留在那里
第三方控制台和仪表盘 你为了让某个服务替你调用提供商而保存在其中的密钥。此时密钥的安全程度就等于该服务的安全程度

最后一行正是这起事件所指向的。当你把提供商密钥保存在另一家公司的控制台里,就是把那家公司的安全性叠加到了密钥的安全性上。这可能是一笔不错的交换,因为有专门团队的服务可能比一台笔记本电脑更好地保护密钥。但这也意味着,该服务中的一个缺陷,可能暴露那些在自己机器上一切都做对了的客户的密钥。

本文不描述这些泄露是如何被利用的,也不会这样做。防御措施并不依赖于此。

如何判断密钥正在被滥用?

迹象出现在提供商一侧,所以应该去看各提供商的控制台。请留意:

  • 你的工作没有变化而支出上升。 两天之间突然跳升,或在无人工作的时段持续增长。
  • 你不使用的模型。 如果你只调用一个模型,而用量页面显示的是另一个,说明密钥正被别人使用。
  • 异常时段或异常规模的用量, 例如整夜保持在高而平稳的速率。
  • 与密钥用途不符的用量。 为某个工具创建的密钥,在该工具关闭时仍有流量。
  • 意外的扣费或触及你没想到会触及的限额。
  • 提供商的通知。 提供商也会检测滥用,可能会给你写信或停用某个密钥。

这些迹象单独都不能证明被盗。每一条都是需要核查的理由。让它们可见的最便宜办法,是在需要之前就设好提醒。

作为用户,如何保护你的密钥?

目标是让被盗的密钥造成的损害很小、持续时间很短,并且你能很快发现。以下措施要配合使用:

  1. 每个工具、每种用途使用一个密钥。 密钥泄露时你知道出自何处,可以在不影响其他一切的情况下吊销它,它的用量页面也能讲清楚一段完整的情况。提供商把密钥按项目或 workspace 分组,这是分隔环境的好办法。OpenAI 的生产最佳实践建议为预发布和生产使用不同的项目,这也允许为每个项目设置各自的速率和支出限制。
  2. 设置支出上限和提醒。 OpenAI 的文档介绍了用量超过某个美元金额时通知你的提醒,以及支出达到上限时停止流量的硬性限额。硬性限额是把没有上限的损失变成有上限损失的控制手段。把它设得比你认为需要的更低,达到时再上调。
  3. 为密钥设置有效期并轮换。 OpenAI 建议在创建项目密钥时设置过期日期,并建立定期轮换的流程:创建替代密钥,更新你的应用,确认它正常工作,然后吊销旧密钥。Google 的文档建议定期轮换密钥,指出不受限制的 API 密钥是不安全的,并建议把每个密钥限制在它所需的 API 上。
  4. 有任何疑问就吊销,而不只是替换。 如果你使用的工具通报了事件,而你不确定自己的密钥是否牵涉其中,就吊销该密钥。创建新密钥只需要一分钟。然后查看旧密钥在吊销前几天的用量。
  5. 不要让密钥出现在代码、仓库和日志中。 像 OpenAI 建议的那样,使用环境变量或密钥管理器,并把 .env 排除在版本控制之外。
  6. 除非服务需要,否则不要把密钥存放在第三方控制台。 如果工具在你的机器上运行,密钥就可以留在你的机器上。如果服务必须替你调用提供商,就使用专为此创建的密钥,配有独立的项目、支出上限和有效期。
  7. 不时检查密钥列表。 删除你不再使用的工具的密钥。提供商的控制台会显示每个密钥的创建时间,部分控制台还会显示最近使用时间。例如,Anthropic 的 Admin API 会返回每个密钥的状态、创建时间和过期时间,并且从不返回密钥本身。

构建 AI 工具的人应该怎么做?

如果你的产品保存属于客户的密钥,你就承担了用户笔记本电脑所没有的责任,设计应当体现这一点。

  • 用托管的密钥服务加密存储的密钥, 例如云 KMS,并用访问控制把可以解密的人和服务,与只负责运行产品的人和服务分开。只存储调用提供商所需的内容。
  • 在各组件之间遵循最小权限。 调用提供商的那部分系统,应当只能读取眼前这个请求所需的密钥,而不是整个存储。
  • 密钥保存后不再显示。 显示一个提示,例如最后几个字符,并让用户替换它。Anthropic 的 Admin API 中的密钥对象就是这样做的:只有部分提示,没有密钥本身。
  • 在你这一侧实施按密钥的配额和异常检查。 你比任何单个客户更早看到所有客户的流量。一个密钥突然发出数千个形态相同的请求,值得暂停并询问。
  • 淘汰旧系统。 仍然可以访问、但已不再活跃使用的代码和数据存储,往往得到的审查最少。把它们删除,或让它们接受与在线系统相同的控制。
  • 让轮换变得容易。 如果吊销并替换密钥是个冗长的流程,用户就不会去做。
  • 尽早、清楚地通知客户, 并说明接下来该做什么。一条简短的消息,说明哪些用户受到影响、需要轮换什么,能让人们在调查结束前就采取行动。

zn-gate 对此能做什么,不能做什么?

zn-gate 是一个开源的安全层,运行在你的机器上,位于你的编程智能体与它接触的工具和内容之间。它帮助解决问题中位于你机器上的那一部分,不能解决位于别人云端的那一部分。我们把两方面都写明,以免你把它当成能做它做不到的事。

它能做的。 以下说法见于 zn-gate 的 README 和 Scrubber 与密钥保险库文档:

  • 借助 Claude Code 插件,或 zn 面向 Codex CLI、Cursor 和 OpenCode 的 hooks,编程智能体无法读取含有真实值的 .env 文件,以及 ~/.aws/credentials 之类的凭据文件。这是一道防护栏,不是沙箱。
  • 智能体的工具所返回内容中的密钥,例如文件读取和命令输出,在模型看到之前会被替换为标记,粘贴到提示词中的密钥会被拦截。在 Cursor 中,只有 MCP 工具的输出会被清理。
  • zn env shadow .env 把你的值移入你机器上的加密保险库,并在文件中留下 __ZN_STRIPE_KEY__ 这样的标记。保险库是本地的,不会同步。
  • 可以在模型看不到密钥的情况下使用它。你允许的命令通过 zn run 获得值,其输出会被屏蔽。对于 HTTP API,secrets MCP 服务器只把值发送给你允许的主机,并在返回响应时把其中的密钥替换为标记。没有任何工具会把值返回给智能体。

它不能做的。

  • 它不保护存放在第三方云端的密钥。在上述事件中,面临风险的密钥是保存在托管控制台 workspace 里的那些。zn-gate 不在该服务与提供商之间的路径上,因此无法减少这种暴露。只有该服务和你,通过限制并轮换该密钥,才能做到。
  • 它不能替代支出上限、提醒或轮换。这些是你在提供商处的控制手段。
  • 它不能阻止你自己启动的进程,也不能阻止以你的用户身份运行的恶意软件。它的威胁模型指出,任何以你的操作系统用户身份运行的程序,都可以按名称向已解锁的保险库索取密钥,并建议在不使用时用 zn lock 锁定保险库。
  • 你允许的命令在运行期间仍然持有该值。zn 控制凭据去向何处,而不控制被允许的程序用它做什么。
  • 它不检测蒸馏攻击。提供商一侧的流量不在它能看到的范围内。

zn-gate 消除的是一种常见的泄露来源:明文躺在 .env 文件里、可被智能体或提示词注入找到的密钥。以你的用户身份运行的被入侵依赖是另一种情况:见上文的威胁模型。让密钥在你的机器上远离智能体,是有用的,也是上面列出的措施之一。

今天就能使用的检查清单是什么?

面向用户:

  • 每个工具有自己的密钥,位于自己的项目或 workspace 中。
  • 每个密钥都有支出上限,以及金额更低的提醒。
  • 每个密钥都有过期日期,并且你知道何时轮换。
  • 你知道每个密钥存放在哪里:本地环境、保险库、CI 密钥还是第三方控制台。
  • 第三方控制台中的密钥只有服务所需的那些,并且设有上限。
  • .env 文件不在版本控制中,编程智能体无法读取它们(npx -y zn-gate check)。
  • 至少每周查看一次按密钥的用量,并在工具厂商发出任何事件通知后查看。
  • 不再使用的密钥已删除。
  • 如果厂商通报了可能牵涉你的事件:创建新密钥,更新工具,吊销旧密钥,并检查其用量。

面向构建工具的人:

  • 客户的密钥用托管密钥服务加密,解密权限范围很窄。
  • 密钥保存后不再显示。
  • 你这一侧有按密钥的配额和异常提醒。
  • 旧系统和旧存储已被淘汰,或受到相同的控制。
  • 有一套经过演练的流程,用于通知客户并轮换所有已存储的密钥。

本文没有涵盖什么?

  • 除 OpenCode 所说的内容外,本文不描述 OpenCode 事件是如何发生的,因为这些并未公开。
  • 本文不提供任何窃取密钥或实施蒸馏行动的方法。
  • 本文不涉及提供商一侧的检测,除非提供商已经公开发布过。
  • 本文不涉及非 AI 提供商服务的密钥,尽管大多数建议同样适用。

常见问题

什么是蒸馏攻击?

用更强模型的输出去训练一个能力较弱的模型。Anthropic 将非法蒸馏描述为竞争对手借此以远低于自行研发所需的时间和成本获得能力。它需要向更强的模型发出数量极大的请求。

攻击者为什么要窃取 API 密钥来发动蒸馏攻击?

因为请求需要付费,也必须从某处发出。用被盗的密钥,账单由密钥的主人承担,流量分散在许多账户上,而不是集中在一个提供商能发现并封禁的账户上。

我怎么知道自己的 AI 提供商密钥是否被滥用?

留意这些迹象:你的工作没有变化而支出上升、请求了你从不使用的模型、在无人工作的时段有用量、或来自你不认识的工具的流量。支出提醒和按密钥查看的用量是最早的信号。

如果我使用的工具发生了密钥泄露,该怎么办?

创建新密钥,在需要它的工具中更新,吊销旧密钥,然后检查旧密钥在吊销之前那段时间的用量。即使不确定你的密钥是否受影响,也应这样做。

zn-gate 能保护我存放在第三方云控制台中的密钥吗?

不能。zn-gate 运行在你的机器上,阻止编程智能体读取你的 .env 和凭据文件,让密钥值不进入模型看到的内容,并且可以使用本地保险库中的密钥而不向模型展示。你粘贴到第三方服务中的密钥不在它的覆盖范围内。

我应该把提供商密钥存放在某个工具的云控制台里吗?

只有在该工具需要它们来替你运行服务时才这样做。如果工具可以在你的机器上运行,密钥放在你自己的环境或本地保险库中,就少一个需要保护的地方。如果你确实要存放在那里,请为它设置支出上限,并使用专用于该用途的密钥。

API 密钥应该多久轮换一次?

OpenAI 的指南建议在创建项目密钥时设置过期日期,并建立定期轮换的流程。频率取决于一个密钥可能造成多大损害:支出上限较低的密钥可以比没有上限的密钥轮换得少一些。


关于如何让 .env 远离智能体的基础知识,请参阅如何用一条命令阻止 Claude Code、Codex CLI、Cursor 和 OpenCode 读取你的 .env。想了解智能体 hook 曾如何被绕过以及如何修复,请阅读编程智能体曾如何间接读取你的 .env 文件。zn-gate 采用 MIT 许可,在本地运行,无需账户:GitHub 和 Install。

分享