> ## Content Index
> Fetch the complete content index at: https://www.hubwiz.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# API Key 是怎么被 LLM 读走的：三个模型实测与四道拦截
- URL: https://www.hubwiz.com/blog/llm-api-key-leakage-agent-context/
- Published: 2026-10-23T17:45:02.000Z
- Updated: 2026-10-24T04:07:28.000Z
- Description: 不用把 key 粘进 prompt：agent 读一个 .env 就够了。三个黑盒模型被还原出真 key，而 AI 辅助提交的泄露率是 3.2%。
- Author: admin
- Tags: AI 安全, API Key, 密钥管理, AI Agent, 数据泄露

**一句话结论**：把 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](https://www.hubwiz.com/blog/litellm-gateway-api-key-theft/) ｜ [Agent / MCP / 自动化完全指南](https://www.hubwiz.com/blog/agent-stack/) ｜ [Agent 安全：九起真实事故，六项通用控制](https://www.hubwiz.com/blog/agent-security-nine-incidents-six-controls/) ｜ [MCP 供应链体检](https://www.hubwiz.com/blog/mcp-registry-supply-chain-audit/) ｜ [Claude Code 的 bash 规则为什么拦不住](https://www.hubwiz.com/blog/claude-code-bash-deny-rule-bypass/) ｜ [开源模型后门：投毒 1% 就能让编码代理泄露 .env](https://www.hubwiz.com/blog/open-model-backdoor-codex-exfiltration/) ｜ [Agent 成本体检：上下文预算与提示缓存](https://www.hubwiz.com/blog/agent-cost-audit-context-budget-prompt-cache/)

**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/](https://blog.gitguardian.com/llm-api-key-leakage/?ref=hubwiz.com)
- *Practical Secrets Extraction against Black-box LLMs*, arXiv:2609.36941：[https://arxiv.org/html/2609.36941v1](https://arxiv.org/html/2609.36941v1?ref=hubwiz.com)
- GitGuardian, *State of Secrets Sprawl 2026*：[https://www.gitguardian.com/state-of-secrets-sprawl-report-2026](https://www.gitguardian.com/state-of-secrets-sprawl-report-2026?ref=hubwiz.com)
- GitGuardian AI Hooks（原文推介的确定性拦截方案）：[https://blog.gitguardian.com/ai-hooks-ai-coding-assistants/](https://blog.gitguardian.com/ai-hooks-ai-coding-assistants/?ref=hubwiz.com)
- 对照阅读（本站）：[Agent 安全九起事故](https://www.hubwiz.com/blog/agent-security-nine-incidents-six-controls/)、[MCP 供应链体检](https://www.hubwiz.com/blog/mcp-registry-supply-chain-audit/)、[Claude Code bash 绕过](https://www.hubwiz.com/blog/claude-code-bash-deny-rule-bypass/)

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

> **这是「Agent / MCP」专题的一部分。**完整地图见：[Agent / MCP / 自动化完全指南](https://www.hubwiz.com/blog/agent-stack/)