API Key 是怎么被 LLM 读走的:三个模型实测与四道拦截

不用把 key 粘进 prompt:agent 读一个 .env 就够了。三个黑盒模型被还原出真 key,而 AI 辅助提交的泄露率是 3.2%。

分享
API Key 是怎么被 LLM 读走的:三个模型实测与四道拦截

一句话结论:把 API Key 粘进 prompt,只是最笨的一种泄露方式。更常见的情况是:agent 为了回答你,会自己去看 .env、翻云配置、读日志、跑命令、调 MCP——它根本不需要你把 key 交出来。GitGuardian 汇总的一手研究显示,只靠模型 API 的输出,就能从三个黑盒模型里还原出真实的 DeepSeek 和 Google key;每一个还原出来的 key,都能在 GitHub 上找到公开对应。

披露:本文编译自 GitGuardian 博客《Leaked API Keys in LLMs: How AI Agents Expose Secrets》(2026-10-19,作者 Dwayne McDaniel)。原文是安全厂商视角,末尾推介自家 AI Hooks 与 Developer Endpoint Protection;文中研究数字与结论均按原文口径,本文未独立复现。黑盒提取研究原文为 arXiv:2609.36941《Practical Secrets Extraction against Black-box LLMs》。


一、三个模型,六把真 key

研究者做的事很简单:只拿模型的输出,不给权重、不给 token 概率,看能不能从回答里抠出真实凭证。结果三个模型全部失守:

模型 还原出的 key 交叉验证
Qwen3-27B 2 个 DeepSeek + 1 个 Google 每个完整 key 都能在 GitHub 找到公开出现
GPT-Codex 5.3 1 个 DeepSeek 同上
Claude Haiku 4.5 2 个 DeepSeek 同上

这件事最关键的一点不是“模型会编造看起来像 key 的字符串”,而是它复现的是真实存在过的字符串。研究者拿到后把 key 遮蔽,没有去测试还能不能用。攻击者不会停在这一步:拿到就可以试、可以接下游系统、可以一直用到被吊销。

只读、黑盒、只看输出,就足以把记忆里的凭证捞出来。

1.1 为什么“输出里能还原”比“训练数据里有”更麻烦

过去的泄露是静态的:key 躺在仓库里,扫描器能发现,轮换一次就结束了。黑盒提取改变的是利用方式——攻击者不需要权重,不需要训练管线,只要能和模型对话,就能反复试探、批量套取。原文特别强调,研究者用的是 “output-only access”,而这恰恰是任何一个 API 用户都有的权限。

更麻烦的是,模型吐出来的 key 和普通文本没有区别。DLP 如果只匹配已知前缀,能拦住一部分;但如果模型把 key 拆成两段、加空格、用 base64,或者干脆用自然语言描述“前四位是 sk-,后面是……”,规则就很难覆盖。这不是一个可以靠正则一劳永逸解决的问题。

那这些 key 最初是怎么进到模型里的?答案有两条,一条在训练数据里,一条就在开发者自己的笔记本上。

二、两条来源:训练数据里的旧账,和笔记本上的现货

2.1 公开仓库 → 训练数据

公开代码一旦带 key,就会通过 fork、复制示例、日志、构建产物扩散,最后进入训练或评测数据集。GitGuardian 的统计是:2025 年公开 GitHub 提交里新增 2,865 万个硬编码密钥;其中与 AI 服务相关的凭证达到 1,275,105 个,同比 +81%。

这是一个“旧账”问题:你以为已经删掉的那次提交,可能早就被复制到了别处,也早就被模型读过了。

2.2 开发者端点 → 模型上下文

更近的一条来源是开发机。密钥不只活在 Git 里,还活在这些地方:

  • .env、云厂商配置文件、Kubernetes 配置
  • shell history(一条 export OPENAI_API_KEY=... 就能留很久)
  • IDE 设置、CLI 工具的本地数据
  • 本地日志、临时调试文件
  • MCP 服务器的配置文件

Trellix 把这条路径叫“隔离梯度”:凭证从加密存储 → 进程内存 → 命令行参数 → 本地文件 → 外部系统,每移动一次,能看到它的人就多一层。一个很具体的例子:agent 为了查 Microsoft Graph 的资料,先用凭据助手拿到 OAuth token,再把它塞进 curl 的 Authorization header。请求成功了,但 token 同时出现在进程遥测、agent transcript 和命令记录里。

原文给的两组数字很能说明问题:

  • Claude Code 辅助的公开提交,密钥泄露率 3.2%;同期公开 GitHub 提交的基线是 1.5%。
  • 公开的 MCP 配置文件里发现 24,008 个唯一密钥,其中 2,117 个是有效凭证。

注意,这些数字只统计了“已经提交到公开仓库”的部分。开发机上还有大量永远不会进 Git 的凭据——本地配置、命令历史、应用数据、AI 工具自己的存储。那部分才是更大的池子,也是 agent 最容易碰到的地方。

2.3 两条路在同一个位置汇合

  • 训练数据把过去的泄露带进模型;
  • agent 把当下的密钥从开发环境带进上下文。

模型上下文,正在成为新的“凭证边界”。

三、泄露发生时,你并没有把 key 粘进去

原文举了一个很普通的场景:你让 agent 帮你排查“这个 API 为什么 401”。

它可能会:

  1. 读项目的 .env,看 key 配的是哪个;
  2. 打开云配置,确认环境变量有没有生效;
  3. 搜应用日志,找报错堆栈;
  4. 跑一条 curl,复现请求;
  5. 调一个 MCP 服务器,拿运行时的连接信息。

这五步里任何一步,都可能把凭证放进发给模型的上下文。 而整个过程里,你一次都没有把 key 复制到对话框。

这也解释了为什么传统密钥扫描经常看不见这类暴露:没有 commit,没有 PR,没有 CI 构建。密钥从开发机直接进了 AI 工作流,仓库扫描器根本没有机会看到它。

agent 的自主性越高,能碰到的环境越多,遇到凭据的机会就越多。

四、一个必须分清的区别:进入上下文 ≠ 进入训练

原文特意把两件事拆开:

  • 模型暴露(model exposure):凭证被发到了开发环境之外,进了这次请求的上下文;
  • 模型训练(model training):这段内容被用于训练。

企业级供应商通常会承诺“API 输入不用于训练”,但这不改变暴露已经发生这个事实。凭证一旦离开开发机,安全团队需要回答三个问题:

  1. 它去了哪里?
  2. 可能被保留多久?
  3. 现在还该不该信任它?
训练只是可能的目的地之一;模型上下文才是当下要处理的问题。

所以控制点必须前移:在凭证变成上下文之前拦截,而不是等它进了模型再想办法。

五、三个常见误区

5.1 “我不把 key 贴进去就没事”

你贴不贴只是其中一条路径。agent 自己读文件、跑命令、调工具,都会把凭证带进上下文。真正的边界不是“对话框”,而是“开发环境到模型”的整条链路。

5.2 “供应商说不训练,所以安全”

“不训练”只回答了“会不会进模型参数”,没有回答“这次请求里有没有、被谁看到、留多久”。企业条款、零保留、区域化是三个不同的承诺,签约前要分开确认。

5.3 “仓库扫描过了,CI 也过了”

仓库扫描覆盖的是 commit。agent 的读取、命令、MCP 返回,很多根本不产生 commit。原文的 3.2% 与 1.5%,统计的都是“已经提交”的部分;开发机上的池子比这大得多。

三个误区的共同点,是把“密钥管理”当成了“代码管理”的一部分。密钥的生命周期比代码短,分布也比代码散。

六、四道拦截,位置比强度更重要

原文的核心主张是:用确定性工具,不要用建议。

系统提示词、AGENTS.md、skill、Markdown 规则,本质上都是“请求 agent 不要这么做”。它可能听,也可能不听——过去两年的各种“失控 agent”故事已经反复证明了这一点。Hook 不一样:它在 agent 工作流的固定节点触发,在 agent 的控制范围之外,在系统层面对文件读取、命令执行、工具调用做检查。

四道拦截依次是:

# 位置 做什么 拦不住时会发生什么
1 提交前 扫描即将发给模型的 prompt key 直接进了上下文
2 执行前 检查文件读取、shell 命令、MCP 调用 agent 读到 .env 或让 MCP 返回凭据
3 返回后 扫描工具输出,含密钥的响应不交给模型 凭证以“工具结果”的身份进入上下文
4 端点上 减少明文凭据、接入 vault、用 MDM 批量部署 agent 总能找到可读的 key

第 4 道是治本:可达的明文密钥越少,agent 能泄露的东西就越少。 原文的建议包括用 Developer Endpoint Protection 扫描开发机上的本地文件和存储,通过 Jamf、Iru、Intune 这类 MDM 批量部署,而不是指望每个开发者自己配置;以及把应用需要的密钥放进受管 vault,并让事件上下文能对上“这个 key 是否已经在 vault 里”。

这套机制也不是全覆盖:密钥如果被写进图片、PDF、截屏,或者被模型拆成多段、编码后再发,规则扫描未必能识别;用户在对话里主动粘贴的 key,hook 也未必能判断意图。它的价值不是 100% 覆盖率,而是把默认状态从“会泄露”变成“先检查”。原文的判断是:你无法穷举 agent 的所有行为,但可以把可达的明文密钥变少、把出口收窄、把已经发生的暴露拦住。

一句可以直接抄的话:

把“别把 key 发给模型”从一句建议,变成一次会中止操作的确定性检查。

七、一张可执行的清单(编译者整理)

下面这张表不是原文的清单,是我按原文逻辑补的落地判断,按“离密钥的距离”从近到远排:

层 做法 判断标准
提示词 发送前跑规则/熵检测,命中即阻断 不依赖模型自觉
工具参数 密钥走 header / 本地凭据代理,不进命令行参数 进程列表和遥测里看不到
文件系统 密钥移出工作目录;agent 的 cwd 里不放凭证 cat .env 拿不到东西
工具输出 返回值先过一遍扫描再交给模型 报错日志里的 key 不会顺手带进去
网络 默认拒绝出网,按域名+路径放行 就算读到了也发不出去
日志/转录 记录工具调用,但脱敏;转录单独加密 审计能查,泄露不扩散
端点 MDM 扫描 + vault + 短时令牌 明文凭据数量可度量地下降
供应商 确认不用训练、保留策略、区域 出问题知道去哪追
应急 能按“可能暴露”批量轮换 不用逐个猜

最后一条经常被忽略:轮换能力本身就是一种防护。如果轮换一次要两天,你就不会在怀疑时轮换;如果是一条命令,很多事故可以在十分钟内止血。

7.1 十分钟自查

  1. 列出 agent 能读到的目录,确认 cwd 里没有 .env、~/.aws、~/.ssh;
  2. 检查 MCP 配置文件里有没有明文 key;
  3. 看 shell history 里有没有 export ..._KEY=...;
  4. 关掉不必要的出网,只留下 provider 域名;
  5. 把“疑似暴露”的 key 列出来,确认能在十分钟内完成轮换;
  6. 打开工具调用日志,确认参数里没有 Authorization。

(这一节是编译者补的,不是原文清单。)

八、对我们自己的含义

我们正在做的 TiaLink 是“本地 MCP 网关 + 云端编译服务”的形态,这条链路里至少有三类敏感物:

  1. 授权/激活 token——它证明“这台机器有权限”,不能作为工具参数交给模型;
  2. 客户的 IR / SimaticML——里面有 PLC 标签名、工艺逻辑、设备命名,属于客户资产;
  3. 调用云服务的凭据——如果被 agent 读进上下文,等于把服务入口交出去。
资产 入口 一旦泄露的后果 先做的控制
激活 / 授权 token MCP 配置、命令行、日志 盗用授权、机器绑定失效 进程内注入、不进参数
客户 IR / SimaticML 工具输入、编译请求、错误日志 客户工艺与命名泄露 不进上下文、日志脱敏、区域化
云服务凭据 环境变量、配置文件 服务被滥用、账单异常 短时令牌、出网白名单
工程路径 / 项目名 文件读取、报错信息 泄露客户信息 路径白名单、错误消息裁剪

对应的做法:

  • 授权 token 只在本机进程内使用,通过本地 HTTP header 或凭据代理注入,绝不放进工具参数、绝不出现在命令行;
  • MCP 配置不写明文密钥,改用系统凭据库或短时令牌;agent 能读到的配置文件,就等于能泄露的配置文件;
  • IR 不进 LLM 上下文:agent 生成 IR 时只带 schema 和必要片段,不把客户完整程序当上下文;
  • 编译服务日志不留 IR/XML 原文,错误消息做脱敏,避免“为了排障把客户逻辑写进日志”;
  • 出网白名单:编译服务只连自己的域名,别给它一个可以任意 POST 的出口;
  • 工具调用参数不要走 CLI:进程参数会进 telemetry、进 shell history、进 agent transcript,这也是 Trellix 那篇分析里反复强调的“隔离梯度”。

九、边界

  • 这是厂商博客,GitGuardian 在卖 AI Hooks 和端点防护,文末的推荐有商业动机;研究数字引用的是自家报告与第三方论文,本文未独立核实。
  • 黑盒提取研究只证明了“能还原出真实字符串”,没有验证这些 key 是否仍然有效;原文也明确说研究者主动停在了遮蔽这一步。
  • 3.2% vs 1.5% 是 GitGuardian 的统计口径(公开提交、特定时间段),不能直接外推成“用了 Claude Code 就会多泄露一倍”。
  • “供应商不用于训练”与“不保留、不记录、不跨境”是不同承诺,签合同前要分开确认。
  • 文中模型名称、版本与日期均按原文;这类研究的结论会随模型迭代变化。

相关阅读:LLM 网关被攻破:LiteLLM 三个漏洞如何偷走 provider key | Agent / MCP / 自动化完全指南 | Agent 安全:九起真实事故,六项通用控制 | MCP 供应链体检 | Claude Code 的 bash 规则为什么拦不住 | 开源模型后门:投毒 1% 就能让编码代理泄露 .env | Agent 成本体检:上下文预算与提示缓存

Sources

本文由 hubwiz 编译整理,转载请注明出处。

这是「Agent / MCP」专题的一部分。完整地图见:Agent / MCP / 自动化完全指南