API Key 是怎么被 LLM 读走的:三个模型实测与四道拦截
不用把 key 粘进 prompt:agent 读一个 .env 就够了。三个黑盒模型被还原出真 key,而 AI 辅助提交的泄露率是 3.2%。
一句话结论:把 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”。
它可能会:
- 读项目的
.env,看 key 配的是哪个; - 打开云配置,确认环境变量有没有生效;
- 搜应用日志,找报错堆栈;
- 跑一条
curl,复现请求; - 调一个 MCP 服务器,拿运行时的连接信息。
这五步里任何一步,都可能把凭证放进发给模型的上下文。 而整个过程里,你一次都没有把 key 复制到对话框。
这也解释了为什么传统密钥扫描经常看不见这类暴露:没有 commit,没有 PR,没有 CI 构建。密钥从开发机直接进了 AI 工作流,仓库扫描器根本没有机会看到它。
agent 的自主性越高,能碰到的环境越多,遇到凭据的机会就越多。
四、一个必须分清的区别:进入上下文 ≠ 进入训练
原文特意把两件事拆开:
- 模型暴露(model exposure):凭证被发到了开发环境之外,进了这次请求的上下文;
- 模型训练(model training):这段内容被用于训练。
企业级供应商通常会承诺“API 输入不用于训练”,但这不改变暴露已经发生这个事实。凭证一旦离开开发机,安全团队需要回答三个问题:
- 它去了哪里?
- 可能被保留多久?
- 现在还该不该信任它?
训练只是可能的目的地之一;模型上下文才是当下要处理的问题。
所以控制点必须前移:在凭证变成上下文之前拦截,而不是等它进了模型再想办法。
五、三个常见误区
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 十分钟自查
- 列出 agent 能读到的目录,确认 cwd 里没有
.env、~/.aws、~/.ssh; - 检查 MCP 配置文件里有没有明文 key;
- 看 shell history 里有没有
export ..._KEY=...; - 关掉不必要的出网,只留下 provider 域名;
- 把“疑似暴露”的 key 列出来,确认能在十分钟内完成轮换;
- 打开工具调用日志,确认参数里没有
Authorization。
(这一节是编译者补的,不是原文清单。)
八、对我们自己的含义
我们正在做的 TiaLink 是“本地 MCP 网关 + 云端编译服务”的形态,这条链路里至少有三类敏感物:
- 授权/激活 token——它证明“这台机器有权限”,不能作为工具参数交给模型;
- 客户的 IR / SimaticML——里面有 PLC 标签名、工艺逻辑、设备命名,属于客户资产;
- 调用云服务的凭据——如果被 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
- Dwayne McDaniel, Leaked API Keys in LLMs: How AI Agents Expose Secrets, GitGuardian Blog, 2026-10-19:https://blog.gitguardian.com/llm-api-key-leakage/
- Practical Secrets Extraction against Black-box LLMs, arXiv:2609.36941:https://arxiv.org/html/2609.36941v1
- GitGuardian, State of Secrets Sprawl 2026:https://www.gitguardian.com/state-of-secrets-sprawl-report-2026
- GitGuardian AI Hooks(原文推介的确定性拦截方案):https://blog.gitguardian.com/ai-hooks-ai-coding-assistants/
- 对照阅读(本站):Agent 安全九起事故、MCP 供应链体检、Claude Code bash 绕过
本文由 hubwiz 编译整理,转载请注明出处。
这是「Agent / MCP」专题的一部分。完整地图见:Agent / MCP / 自动化完全指南