安全指南

AI 写的代码泄露了 API key 并被盗刷:原因是未打补丁的 CVSS 10.0 漏洞

个人开发者真实会遇到的模式:借助 AI 上线的应用被偷走 API key,陌生的批量任务把账单刷爆。原因一开始是如何被误判的、真正的原因(公开已久却数月未打补丁的 CVSS 10.0 RCE)是如何找到的,以及如何防止。不包含攻击步骤。

发布于 2026-06-07 更新于 2026-10-06 5 分钟阅读
本页目录

本文把没有安全背景的个人开发者常遇到的一类事故,隐去所有可识别的细节后整理成教训。文中不包含复现攻击的步骤。目的是防御,让你不再犯同样的错误。

事故概要 — INCIDENT FILE
类型
API key 泄露 / 盗刷
严重度
Critical(被利用的是公开的 CVSS 10.0)
根本原因
Web 框架的认证前 RCE 数月未打补丁
泄露范围
环境里的所有 secret
执行范围
局限在非特权容器内(宿主机 root 未受损)
根治措施
①升级到修复版 ②轮换全部 key ③用机器监控 CVE
10.0
CVSS / 最高危等级
数月
未打补丁的时长
整个 env
按已泄露处理的范围
监控
最有效的防止复发手段

「止住了账单」≠「处理完成」

止住账单只是临时措施。封堵泄露途径是另一项工作。两件都做完,才算处理完成。

发生了什么(时间线)

  1. Day 0 — 上线并运行

    借助 AI 写好的应用上线,并在生产环境中运行。
  2. 某一天 — 账单暴涨

    云 AI 的账单突然飙升。跑的是本不会用到的模型,做的是与自己无关的大量批量处理。
  3. 排查 — 第三方滥用

    「自己的应用失控了」根本解释不通。是第三方拿着偷来的 key 在跑。
  4. 追查 — 调查泄露途径

    「那么 key 是从哪里漏出去的?」实际的调查从这里才开始。

为什么一开始没能察觉

按犯错的先后顺序写下来,因为这些错误本身就是教训。

1

过早认定了原因

起初以为「是自己的兜底代码出了故障」。但只要看数据(实际处理了什么),就知道不可能是这样。
→ 在下定论「是自己的错」还是「是别人的错」之前,先看数据。
2

grep 干净,差点就停止调查

把全部代码、git 历史、关联仓库都搜了一遍 → 哪里都没有明文 key。差点以「无从追踪」收手。只看文件,漏掉了运行时的泄露。
3

掩盖症状就以为修好了

在代理层把症状掩盖住,差点就以为处理完了。但掩盖症状,RCE 依然存在。不修复原因,就一直可以被利用。

grep 干净不代表没事

即便任何文件里都没有 key,漏洞也可能从正在运行的进程中抽走环境变量。泄露不只发生在文件里,也发生在运行时(RCE、HTTP 请求头)。详见 什么是 RCE。

实际的原因:公开的 CVSS 10.0 一直没打补丁

把旧访问日志里一个奇怪的特征拿去和威胁情报比对,很快就匹配上了。所用 Web 框架的版本区间存在一个公开的认证前 RCE(CVSS 10.0),并且已经在实际中被利用,而它数月没打补丁一直在运行。

  • 这不是被动的 bug,而是攻击者在服务器上执行代码、把环境变量带走。
  • 限制了损害的一点是:执行范围局限在非特权容器内(宿主机 root 没有被拿下)。入侵调查没有发现常驻后门、挖矿程序或 C2,能确认的影响是 secret 被窃取。
  • 由于内部 DB 是可达的,处置时假定DB 内容也已泄露。
看得见的症状:毫无印象的意外账单
运营者能看到的只有这里
↑ 偷来的 key 事后被转卖或滥用
整个 env(所有 secret)被窃取
↑ 攻击者在运行中的服务器上执行任意代码
原因:公开的 CVSS 10.0 一直没打补丁(认证前 RCE)
意外的账单只是看得见的症状。其下是原因:公开的 CVSS 10.0 一直没打补丁。

教训:出现奇怪的行为时,先怀疑已知 CVE。不要认定是自己的 bug。

缩小泄露范围:那个 key 存放在哪里?

grep 什么也找不到时,就换一种查法。列出被滥用的 key 写在过哪些地方,候选的泄露途径就会变少。例如,如果被滥用的 key 只存在于生产服务器的环境变量中,就应怀疑生产服务器或托管服务商,而不是 Git 或浏览器。

泄露位置常见原因如何检查自己的环境如何封堵
Git 历史 / 公开仓库提交了 .env、把 key 硬编码用 gitleaks git 扫描完整历史。查看 GitHub 的 secret scanning 警报先吊销,再在提交前拦截
发送到浏览器的 JavaScript把 key 放进 NEXT_PUBLIC_ 变量、直接从浏览器调用 API在构建产物中搜索 key 的前缀把 API 调用移到服务端代码
公开的 Web 文件夹.env 或备份放在了公开文件夹里在自己的网站上打开 /.env,确认返回 404 或 403把机密文件移到公开文件夹之外
日志 / 错误页面开着调试输出上线、原样记录请求头检查生产环境的调试设置,以及日志中是否出现 Authorization 等关闭调试输出,并在日志中屏蔽机密
正在运行的服务器公开的漏洞(如 RCE)未打补丁对比实际运行的版本与公告中的修复版本升级,然后更换环境中的所有 secret
托管服务商服务商一侧被入侵查看服务商的安全公告和邮件通知以保存后无法再查看值的方式存放 secret,并轮换
你的开发机恶意软件包或扩展检查你安装的软件包或扩展版本是否与公开的入侵事件吻合移除该版本,并更换这台机器上存放过的 secret

本案例属于「正在运行的服务器」这一行。下面四行都是不会在代码或 Git 中留下痕迹的途径。OWASP 的 Secrets Management Cheat Sheet 也提醒,环境变量往往能被同一环境中的其他进程读取,还可能进入日志或转储。所以,一个 key 被滥用时,要把存放在同一处的所有 secret 都当作已泄露。

分开使用 key,调查会更快

如果每个服务、每个环境(生产与开发)使用不同的 key,仅凭被滥用的 key 就能知道你处在表中的哪一行。一个 key 到处复用,就只能怀疑所有位置。如何拆分 key,见 API key 被盗导致 OpenAI 账号被封。

按实际运行的版本来统计漏洞

检查其他依赖时,报告说「还有 8 个有漏洞的依赖」,这也错了。它是按 package.json 中的下限(^ caret 范围)来统计的。真正危险的,只有遗留下来的 2 个固定版本的依赖。

检查自己实际运行的版本

要看 lock 文件或正在运行的容器中实际解析出来的版本。

# npm: see what's actually installed
npm ls next react react-dom
 
# inside a running container
docker exec <container> npm ls <package>

这里显示的数字才是实际运行的版本,而不是 package.json 里的 ^。

如何检查自己的环境

无论你刚发现奇怪的账单,还是什么都还没发生,都可以按这个顺序检查。每一步只查看你自己的账户和服务器。

1

查看用的是哪个 key、在什么时候

在 AI API 的控制台里,按项目(Anthropic 上是 Workspace)和按天拆分用量和费用。在你从不使用的模型上、或在你什么都没运行的时段产生的用量,说明那个 key 泄露了。然后从表中该 key 存放位置对应的那一行开始查。
2

检查发送到浏览器的文件

在 Next.js 中,以 NEXT_PUBLIC_ 开头的环境变量的值,会在构建时内联到发送给浏览器的 JavaScript 中。构建后,在产物中搜索 key 的前缀,例如 grep -rl "sk-" .next/static。在那里找到的东西,任何人都读得到。
3

确认公开文件夹中访问不到机密文件

对自己的网站运行 curl -sI https://example.com/.env(把 example.com 换成你的域名),确认第一行的状态码不是 200(应为 404 或 403)。详见 检查公开目录中的机密文件。
4

对比生产版本与公告

如上面的方框所示,在生产容器内运行 npm ls 查看版本。osv-scanner scan -L pnpm-lock.yaml 可以检查 lock 文件中是否包含有已知漏洞的版本。
5

检查日志中有没有 secret

对存放应用日志的文件夹运行 gitleaks dir --redact,找出被写进日志的 key。--redact 让找到的值本身不显示在屏幕上。
6

检查上限和提醒

OpenAI 允许在控制台的 limits 页面设置支出提醒和每月硬性支出上限。Anthropic 允许为每个 Workspace 设置支出上限。确认你设置的不只是什么都阻止不了的提醒。

首先做了什么(可复现的步骤)

1

立即吊销被滥用的 key

能止住损失,但这只是工作的一部分。
2

把框架升级到修复版

真正封堵 RCE。升级后,在日志中确认泄露的特征已经消失。
3

轮换所有 secret

假定已泄露。按「在任何地方都能被滥用的优先」的顺序:对象存储 key → 会话签名 key → API key → DB 凭据。
4

入侵调查

检查常驻程序、可疑的 cron、对外的 C2 通信(本次均未发现)。
5

账户侧的防御

修改密码,启用 MFA,检查有没有陌生的 API key。

防止复发:自动监控 CVE

归根结底,这次事故就是「一个公开的 CVSS 10.0 被人为疏忽了」。人工检查总会遗漏,自动监控能发现它们。

今天就能开始的依赖扫描

免费即可开始,在 CI 中加一个步骤即可。

# OSV scanner (Google) checks your lockfile
osv-scanner scan -L pnpm-lock.yaml
 
# On GitHub, enable Dependabot (repo Settings → Security)

本站自己也按照这个教训,用 CVE 监控自身的依赖。我们推荐的做法,我们自己也在做。

常见错误及修正方法

  • 把新 key 放到仍然敞开的服务器上:同一条途径也会把新 key 偷走。立刻停用被滥用的 key,修复漏洞,然后再完成其余 secret 的更换。
  • 把预算提醒当成上限:Google Cloud 明确说明,只有提醒的预算不会自动停止用量或支出。想让它停下来,就要加上能阻止用量的上限,例如 OpenAI 的每月硬性上限或 Anthropic 的 Workspace 支出上限。
  • 把 AI API key 放进浏览器或手机应用:任何到达用户设备的值都可能被取出。从你的服务器调用 AI API,让浏览器调用你的服务器。
  • 从文件或 Git 历史中删掉就到此为止:已经复制了它的人仍然可以使用。先吊销,之后再清理历史(见 为什么 GitHub 上泄露的凭据仍然有效)。
  • 开发和生产使用同一个 key:分不清是哪一边泄露的,只能两边都停。每个环境签发单独的 key。

公开案例:环境变量从服务商一侧泄露

任何文件搜索都找不到的泄露,也会发生在你自己的服务器之外。2026 年 4 月 19 日,Vercel 披露了其内部系统遭到未经授权访问。据该公司称,一名员工使用的第三方 AI 工具被入侵,导致部分客户未标记为「sensitive」(保存后无法再查看值的设置)的环境变量被盗。

Vercel 建议客户检查并轮换未标记为 sensitive 的环境变量,并查看活动日志和最近的部署。它还表示,删除项目或账户并不足以消除风险。

处于这种情况的客户,即使把代码和 Git 搜上一整天,也找不到泄露的痕迹。应对方法与本案例相同:更换存放在那个地方的所有 secret。具体步骤见 Vercel 安全事件(2026 年 4 月)。

教训

常见的错误

  • 「止住了账单」就当作处理完成
  • grep 干净就放心
  • 在代理层掩盖症状,就觉得修好了
  • 按 package.json 的下限来数漏洞
  • 只轮换确认被滥用的那 1 个 key

正确的防御

  • 把止住盗刷和封堵泄露途径当作两件事,都要做
  • 也要怀疑运行时泄露(RCE、请求头)
  • 更新造成问题的框架
  • 按实际运行的版本来判断
  • 一旦泄露,就更换整个 env

总结一下:账单暴涨是症状,原因是一直没打补丁的 CVSS 10.0 RCE。正确的原因并不是第一次就找到的,而是错了几次、反复核实之后才找到的。

接着读

参考资料

  • OWASP, "Secrets Management Cheat Sheet"(环境变量的风险、泄露时的应对):cheatsheetseries.owasp.org
  • Next.js Docs, "How to use environment variables in Next.js"(NEXT_PUBLIC_ 的内联):nextjs.org
  • OSV-Scanner, "Usage":google.github.io
  • OpenAI, "Production best practices"(key 的管理、提醒和上限):developers.openai.com
  • Claude Help Center, "Creating and managing Workspaces"(Workspace 的上限和 key):support.claude.com
  • Google Cloud, "Create, edit, or delete budgets and budget alerts":docs.cloud.google.com
  • Vercel, "Vercel April 2026 security incident":vercel.com

FAQ

QAPI key 泄露了,只把被滥用的那一个吊销就行吗?
A

不行。如果泄露途径在运行时(RCE 或请求头泄露),就要假定环境里的所有 secret 已经一次性全部泄露,并全部轮换。你看到被滥用的那个 key,只是泄露内容的一小部分。

Q把代码和 git 都 grep 一遍,没找到 key 就安全了吗?
A

不一定。即便任何文件里都没有 key,框架的漏洞也可能直接从正在运行的进程中把环境变量抽走。泄露不只发生在文件里,也发生在运行时。

Q最可靠的防止复发的办法是什么?
A

用机器监控依赖的 CVE。这次的原因是一个公开的 CVSS 10.0 被人为疏忽了数月;Dependabot 或 osv-scanner 能自动发现这类问题。

QAI API 的账单突然暴涨,第一步该做什么?
A

先在服务商的控制台里停用被滥用的 key,止住损失。然后根据这个 key 存放的位置缩小泄露途径的范围,修复漏洞或配置错误,之后再更换存放在同一处的所有 secret。只吊销 key,泄露途径依然敞开。

Q怎么知道是哪个 key 泄露了?
A

在 AI API 的控制台里,按项目(Anthropic 上是 Workspace)拆分用量和费用。在你从不使用的模型或时段上产生用量的那个 key,就是泄露的 key。如果每个服务、每个环境使用不同的 key,这个 key 存放的位置也能告诉你泄露途径。

Q设置了预算提醒,盗刷就会停下来吗?
A

不一定。例如 Google Cloud 明确说明,只有提醒的预算不会自动限制用量或支出。要真正停止使用,需要设置能阻止用量的上限,例如 OpenAI 的每月硬性支出上限,或 Anthropic 按 Workspace 设置的支出上限。