跳到主要内容

你的机密管理器在上下文窗口开始的地方终结

· 阅读需 13 分钟
Tian Pan
Software Engineer

你的 Vault 表现无可挑剔。秘密在静态存储时被加密,访问被记录,轮转是自动化的,没有任何明文会触碰磁盘。接着,你的 Agent 调用了一个调试工具,该工具将环境变量转储打印到标准输出(stdout),框架体贴地将标准输出送入模型的上下文——于是你的数据库凭据现在成了 Prompt 的一部分。从那一刻起,Vault 的保证就成了虚构。该凭据存在于你的可观测性平台捕获的 Trace(追踪)中,存在于你的提供商根据该前缀建立索引的 Prompt 缓存中,存在于你的 Agent 在会话之间写入的内存存储中,存在于某人从生产流量中快照得到的评测固件(eval fixture)中,以及提供商的保留日志中。五个持久化层,你的凭据管理器甚至不知道它们的存在。

这并非假设。一项针对超过 17,000 个已发布的 Agent 技能的大规模研究发现,通过日志导致的信息泄露占所有凭据安全问题的 73.5%——远超占比 18.2% 的硬编码密钥——这正是因为 Agent 框架会将控制台输出直接捕获到 LLM 上下文窗口中。旧的失败模式是开发者将密钥提交到 GitHub。新的失败模式是工具响应将密钥提交到上下文窗口,而上下文窗口没有 git revert

一个令人不安的界定:流经模型的凭据无法撤销泄露。你可以轮转它,但你无法轮转那些副本。每一个重要的缓解措施都源于对这句话的严肃对待。

上下文是复印机,而非管道

工程师在思考凭据时,总以为模型调用就像一根管道:数据进入,响应传出,然后数据消失。实际的架构是一台带有五个输出托盘的复印机。

Traces (追踪)。 每个严肃的 LLM 部署都会运行可观测性工具——LangSmith、Langfuse、Braintrust 或自建的 Span 记录器。这些系统默认捕获完整的请求和响应 Payload,因为这正是它们对调试有用的原因。Prompt 中的秘密现在成了追踪后端的秘密,受该系统的保留策略、访问控制和导出集成的约束。堆栈追踪泄露本身就是一个子类别:当推理客户端抛出异常时,异常消息通常会嵌入失败请求的 API 密钥,而该异常会流入捕获它的任何日志管道。

Prompt 缓存。 提供商会缓存 Prompt 前缀以降低延迟和成本,你的网关可能还会在其上进行激进的缓存。包含凭据的缓存前缀的寿命超出了创建它的请求。缓存条目被建立索引、存储并按照你无法控制且大多无法检查的时间表被剔除。

内存存储。 长周期 Agent 会持久化它们学到的内容。如果凭据出现在工具响应中,Agent 很有可能将其总结到长期记忆中——“测试数据库密码是 X”正是内存系统旨在保留的那类事实。内存写入是由模型介导且非结构化的,因此没有 Schema 能告诉你哪些行包含秘密。

评测固件 (Eval fixtures)。 团队会快照生产环境的 Trace 以构建回归测试套件。带有嵌入式凭据的 Trace 变成了带有嵌入式凭据的固件,被检入仓库,复制到笔记本电脑,并在接下来的两年里在 CI 中反复回放。评测集是半衰期最长且安全审查最少的持久化层。

提供商日志。 API 提供商保留输入和输出以进行滥用监控——历史上行业默认是 30 天,尽管这正在发生变化;Anthropic 在 2025 年底将标准 API 日志保留期缩短至 7 天,并且针对符合条件的企业的客户存在零数据保留协议。但除非你签署了协议,否则你的凭据已经在受他人保留策略管辖的基础设施上停留过。

你的 Vault 审计日志记录了一次读取。复印机产生了五个副本。这种不对称性正是问题的核心。

轮转假设存在一个你可以撤销的源头

泄露凭据的标准事件响应是:轮转它,使旧凭据失效,搞定。这之所以奏效,是因为传统的泄露发生在一个可数的地点集合中——仓库、日志文件、Slack 消息——而轮转能同时让所有副本失效。

对于上下文泄露,轮转在且仅在一个意义上仍然有效:旧凭据停止身份验证。它不能告诉你的是 凭据在上下文活跃期间还接触了什么。持有有效密钥的 Agent 在会话中途可能会被 Prompt 注入,从而迫使其使用该密钥——而注入导致的外泄已不再是理论。一次公开披露显示,仅仅是一个恶意的 Pull Request 标题,就能让来自三个不同供应商的代码 Agent 将它们自己的环境变量作为 PR 评论发布。Agent 读取了攻击者控制的文本,文本索要秘密,而 Agent 正好有秘密可以提供,因为这些秘密就躺在它的环境和上下文中。

“凭据进入上下文”与“凭据被轮转”之间的窗口期是一个攻击窗口,在这个窗口中模型本身就是一个“混淆代理”(confused deputy)。大多数团队甚至无法衡量这个窗口,因为他们根本不知道凭据进入了上下文。没有任何警报。工具调用成功了。除非有人去阅读 Payload,否则 Trace 看起来很正常。

这也是为什么规模化趋势令你担忧。GitGuardian 的 2026 年蔓延报告统计显示,2025 年有 2865 万个新秘密流向公共 GitHub——AI 辅助的提交泄露率约为基准率的两倍,仅在与 MCP 相关的配置文件中就暴露了超过 24,000 个独特的秘密。同一份报告发现,2022 年确认有效的凭据中,近 70% 在三年后 依然有效。将长效凭据与复印机结合,你就会得到复合式的风险暴露:秘密泄露一次,到处持久化,并持续有效。

工具边界是唯一的真实卡点

一旦你接受了上下文是不可逆的这一事实,设计问题就会发生反转。不要再问“我该如何从追踪记录中清除密钥?”,而要开始问“密钥在哪个最后环节仍是可操作的结构化数据?”

答案就是工具边界——即工具响应在附加到 Prompt 之前进行序列化的衔接处。在这个衔接处之前,凭据是你所控制的运行时中的一个类型化值。而在之后,凭据就变成了概率系统内部无差别的文本,并已经发散到了那五个托盘中。衔接处下游的一切都只是尽力而为的清理;衔接处的一切才是强制执行。

具体来说,工具边界是三道防线的所在地:

  • 出口脱敏 (Egress redaction):扫描每个工具响应中的凭据模式——高熵字符串、已知的密钥前缀(sk-ghp_AKIA)、连接字符串格式——并在框架看到输出之前将匹配项替换为占位符。针对 Agent 技能研究给框架作者的首要建议正是这一点:在将 stdout 注入上下文之前,提取其中识别出的凭据模式。正则表达式无法捕获所有内容,但它能捕获目前通过 console.log 泄露的 90% 的内容。
  • 占位符解引用 (Placeholder dereferencing):反转流程,使密钥根本不需要进入上下文。模型使用符号引用(如 {{STRIPE_KEY}}),由受信任的执行器在调用时、在模型视线之外进行解析。模型可以编排它从未见过的凭据的使用。这就是我们几十年来在 Web 服务器和数据库之间使用的权限分离,现在应用到了推理引擎和执行引擎之间。
  • 传输层注入 (Transport-layer injection):最强版本是将凭据完全从 Agent 的进程中移除。本地代理或内核模块拦截 Agent 的出站 HTTP 请求,通过其身份(SPIFFE 或类似协议)验证工作负载,从代理商处获取短效 Token,并在请求离开 Agent 后注入认证标头。像 Infisical 的 agent-vault 和该领域的工作负载身份产品都趋向于同一种形态:Agent 证明自己的身份;基础设施提供其所需;凭据值从未存在于模型可以读取或回显的任何地址空间中。

请注意,不在此列表中的内容是:对模型响应进行输出过滤。过滤模型的输出只是针对一种外泄渠道的防护措施;它对追踪记录、缓存、内存存储或供应商日志毫无作用,因为这些在密钥进入的过程中就已经捕获了它。

短期凭据:无需手动触发的轮转

脱敏是一个卡点,但卡点会被绕过——一个新的工具、一种新的输出格式,或者正则表达式遗漏的 Base64 编码数据块。补充性的控制手段是在构建时让泄露的产物失效:如果每个凭据都在几分钟内过期,那么在有人读取追踪记录中捕获的副本时,它已经成了化石。

这是在 Agent 系统中使用工作负载身份和短期 Token 的核心论点,值得精准表述:短期凭据并不能防止泄露;它们将泄露的爆炸半径限制在 Token 的生命周期内。一个出现在评估固件中的 15 分钟 Token 只是一个无效字符串。而出现在同一位置的静态 API 密钥则是一个长期负债,其有效期可能长达那 70% 依然有效的统计数据所暗示的年限。

权衡是真实存在的。Token 的签发会增加工具调用的延迟。代理商(Brokers)变成了关键路径基础设施。运行时间较长的 Agent 任务需要任务中途的重新认证,这意味着你的 Agent 运行时需要处理凭据刷新而不丢失进度。但这些成本中的每一个都是具有已知形态的工程问题,而“哪些追踪记录包含我的 Postgres 密码”则是一个没有终止条件的考古学问题。

一个有用的强制机制:将任何寿命超过你最长 Agent 会话的凭据视为设计异味(design smell)。如果 Agent 的寿命能超过轮转周期,那么轮转周期就在发挥作用;如果凭据的寿命能超过 Agent,那么 Agent 构建的每一个上下文都是你攻击面的一部分。

没人能回答的审计问题

这里有一个测试,可以区分真正内化了这一点的团队和没有内化这一点的团队。请问:模型已经见过哪些密钥?

你的 Vault 可以回答“谁在什么时候读取了哪些密钥”。你的 SIEM 可以回答“日志文件中出现了哪些密钥”。但几乎没有人能回答上下文窗口版本的提问,因为回答它需要扫描每一个存储的追踪记录、每一个缓存的前缀、每一个内存行以及每一个评估固件,以查找你使用的每种凭据格式——而且是追溯性地跨越由四个不同团队(其中一些是外部团队)拥有的系统进行扫描。

如果你无法回答这个问题,你就无法界定事故的影响范围。当一个密钥泄露时,“这个密钥是否曾经过模型上下文,如果是,出现在哪些会话中,当时还有什么其他内容”之间的区别,决定了你是只需要轮转一个凭据,还是需要审计一个被注入的 Agent 可能采取的每一项行动。

让这个问题变得可回答主要是一个记账工作,这就是为什么值得现在就做,而不是等事故发生时再做:

  • 在衔接处打标签:工具边界脱敏层已经可以检测凭据模式;让它在每次检测到凭据经过时(即使脱敏成功)都发出一个结构化事件——密钥指纹(哈希值,绝不是原始值)、会话 ID、时间戳。如果追踪记录中出现了指纹,但在事件中没有匹配项,那么脱敏失败就变得可检测了。
  • 对追踪记录进行指纹化:对你的追踪存储和内存存储运行与 Git 提交相同的密钥扫描,持续进行。供应商提供了追踪掩码钩子(如 LangSmith 的输入/输出掩码,带有凭据正则的 SDK 匿名器);差距通常在于没人开启它们,也没人扫描在开启之前已经存储的内容。
  • 盘点第五个托盘:像了解数据库备份计划一样了解你的服务商的保留条款。七天、三十天、零数据保留合同——这决定了事故的暴露窗口在超出你可以查询的基础设施之外会延伸多久。

的发展方向已经足够清晰。密钥扫描在五年左右的时间里,从“偶尔在你的仓库上运行一下”变成了强制性的预提交门槛。上下文出口扫描也将走同样的道路,而且会更加紧凑,因为 Agent 产生泄露表面的速度比仓库快得多——AI 服务凭据泄露量同比飙升 81%,这说明复印机已经运转起来了。那些能够安然无恙的团队,是那些将卡点设在工具边界、将凭据寿命限制在会话寿命之下,并能通过查询而非作战室(war room)来回答审计问题的团队。Vault 从来不是边界。Prompt 才是。

References:Let's stay in touch and Follow me for more thoughts and updates