过去一年反复撞上的同一个工程问题

过去一年,任何认真把 MCP 工具调用推到生产环境的企业,几乎都会撞上同一个问题:Agent 要调用一个需要凭据的工具,但 Agent 不应该持有这个凭据。这个问题的传统答案是"给 Agent 一个 token",但这个答案在过去一年的多起 Agent 安全事故里被反复证明不够——token 进了 Agent 上下文,就成了 Agent 视角下"自己持有的凭据",Agent 被劫持后 token 就跟着被泄漏。

问题的工程根因在于,凭据持有权和凭据使用权被错误地绑在一起。Agent 持有 token 的那一刻,它既有了"使用工具的权利",也带了"泄漏 token 的风险";而"使用工具"是 Agent 必须做的事,"泄漏 token"是必须规避的事,两者绑定在同一个对象身上,任何对"使用"的优化都会同步放大"泄漏"的风险。这条结构性矛盾在过去一年的多份独立工程实践中被反复验证,并各自给出了一些工程化的应对路径。

第一类应对:服务端策略网关

第一类应对把策略检查放在 MCP server 端,把凭据留在 server 进程里。2026 年 9 月中旬开源的一个项目是这条路径的工程化样本:它围绕一个具体问题展开——如何让 Agent 调用一个需要凭据的工具,但又不把凭据交给 Agent。示例里定义两个工具,一个开放、一个受保护;当 Agent 调用受保护工具时,中间件先查策略——策略 ALLOW 才执行工具,REJECT 直接拒绝;整个过程中凭据始终留在 server 进程里,Agent 从头到尾没见过这个凭据。

这套设计的工程关键是把"授权决策"和"工具执行"用中间件分隔开。中间件本身只做一件事:查策略;策略可以基于调用者身份、基于调用上下文、基于时间窗口、基于资源配额,任意组合。中间件查到 ALLOW,工具正常执行;中间件查到 REJECT,工具返回明确的拒绝原因。Agent 看到的只是一次"成功"或"被拒"的调用,看不到凭据、看不到授权过程、看不到策略细节。

这套设计的工程价值在于,凭据完全活在 server 进程内,生命周期由 server 控制——server 重启凭据需要重新加载,server 配置变更凭据需要重新部署,任何对凭据的修改都走 server 的标准运维流程;Agent 进程对凭据的存在性、形态、变更时机完全无感知。这种"凭据归 server 管,调用归 Agent 管"的分层,是过去一年所有生产级 MCP 部署都会收敛到的工程基线。

第二类应对:JS 代码代理执行

第二类应对把"凭据注入"嵌在脚本执行环境里,Agent 只持有不可解密的 opaque ID。这条路径在 HN 社区里和 Keydris 同期被另一团队独立探索出来:Agent 不直接调用外部 API,而是写一段 JavaScript 代码,在代码里使用一个 opaque identifier 作为 header;代码被发到一个执行环境,执行环境在运行前把 opaque identifier 替换成真实 token;然后这段 JS 实际发起 API 调用、转换结果、处理响应。整个过程中,Agent 看到的是一段带 opaque ID 的 JS 代码,真实 token 只活在执行环境里,且执行结束后立即销毁。

这套设计的工程关键是把"Agent 生成调用代码"和"代码执行"拆成两个独立的工程组件。Agent 的角色是"生成调用代码",凭据完全不在它视野内;执行环境的角色是"实际发起 API 调用",凭据只在它内存里存活。两个组件之间的接口是"带 opaque ID 的代码 + 实际上下文",这一接口对 Agent 完全无害,任何 leak 出去的代码都不携带真实凭据。

这套设计的工程价值在于,它把"凭据使用"和"代码生成"解耦到了两个不同信任级别的主体——Agent 在不可信侧,执行环境在半可信侧,真实 API 在可信侧。Agent 即使被完全劫持,能产出的最坏结果是"生成一段带 opaque ID 的代码",攻击者拿到这段代码后还需要攻破执行环境才能拿到真实凭据,攻破路径比直接读 Agent 上下文长得多。

第三类应对:网络代理 + 凭据替换

第三类应对把凭据替换放在网络层,通过强制代理拦截所有出网请求,在请求出网前按白名单替换 placeholder 为真实凭据。这条路径在 2026 年 2 月初出现的 Airut 项目里有完整实现:Agent 跑在 rootless Podman 容器里,容器持有 surrogate tokens——形态和真实凭据一致,但携带的是不可解密的随机数据;容器的所有出网请求被 mitmproxy 强制拦截,代理在请求出网时按预设白名单做凭据替换,响应回传时剥掉响应里的敏感字段;真实凭据只在代理的内存里存活,从落盘到持久化存储。

这套设计的工程关键是把"凭据替换规则"和"Agent 代码"完全解耦——替换规则由治理侧在外部 YAML/JSON 文件里集中定义,Agent 代码和提示词不能修改这些规则;Agent 持有的 surrogate token 看起来像真凭据,但调用时会经过代理按规则替换为真凭据,Agent 自己不知道也不需要知道替换细节。这条设计的优势是对 Agent 透明——Agent 代码不需要重写,也不需要适配任何特殊 SDK,Agent 仍然按"调用一个有 token 的 API"的常规思路写。

这套设计的工程价值在于,它和现有的容器化 Agent 部署完全兼容——任何已经在容器里跑 Agent 的企业,只需要在容器和外部网络之间插入一个强制代理,就能把凭据从 Agent 进程里隔离出去。改造工作量极小,但安全收益显著,因此成为过去一年最容易被企业接受的工程路径。

第四类应对:macaroon 能力令牌

第四类应对把"调用授权"本身做成密码学签发的令牌,凭据和能力分离。这条路径在 SatGate 项目里有完整实现:Agent 不持有任何看起来像凭据的东西,而是持有 macaroon 能力令牌——基于 HMAC 链的加密令牌,令牌本身嵌入 caveat(限制条件);caveat 可以是"这个令牌只能用于工具 X"、"调用频次上限 Y"、"时间窗口 Z"、"目标资源范围 W";Agent 调用 API 时,网关验证令牌上的 caveat 是否满足,满足则放行,不满足则拒绝。

这套设计的工程关键是把"凭据"和"授权"拆开。Agent 持有的不是凭据,而是"调用 API 的授权";真实凭据仍在外部服务侧,Agent 通过出示 macararon 令牌让网关代为调用外部服务。整个过程中,Agent 见不到任何真实凭据——它持有的只是一段加密令牌,即便被泄漏,攻击者拿到的也是受限的、即将过期的、不可解密的加密字符串。

这套设计的工程价值在于,它把"凭据生命周期"和"任务生命周期"对齐——每次 Agent 任务开始前申请一个针对本次任务的 macaroon 令牌,任务结束立即撤销;令牌在 Agent 进程内不持久化,跨任务不重用。这条机制把"凭据残留"的窗口期压到接近零,是所有方案里最严格的。

四条路径的工程取舍

四条路径解决的是同一组问题的不同切面,工程取舍各有侧重。服务端策略网关适合"现有 MCP server 可以加中间件"的场景——只要在 server 里加一层策略检查,Agent 代码不需要改一行;代价是每个 MCP server 都得单独部署中间件,管理成本随 server 数量线性增长。JS 代码代理执行适合"Agent 已经能写代码"的场景——代码执行环境的开发量相对大,但隔离粒度细,适合高敏感场景。网络代理 + 凭据替换适合"现有 Agent 已经容器化"的场景——插入强制代理即可,改造最小;代价是代理本身成为新的信任边界,代理被攻破则所有 Agent 的真实凭据都暴露。macaroon 能力令牌适合"Agent 从设计阶段就考虑安全"的场景——隔离最彻底,但 Agent 代码需要重写,适配工作量最大。

在企业落地的工程实操上,通常的取舍是"网络代理优先、能力令牌跟进、服务端策略网关兜底"。第一阶段用网络代理把现有 Agent 全部包起来,凭据先不交到 Agent;第二阶段逐步把 Agent 改造成能力令牌持有者,把网络代理退役;第三阶段在 MCP server 侧加策略中间件,做最后的兜底。这条路径在工程上是渐进式的,不要求企业停下所有 Agent 业务去重写一遍。

企业 Agent 凭据隔离的工程新基线

把四条路径汇总,可以画出企业 Agent 凭据隔离的工程新基线。这条基线由五条构成,缺一不可。

第一条,凭据持有权和凭据使用权必须解耦。Agent 持有的是"使用 API 的权利",不是"凭据本身";任何"把真实凭据放进 Agent 进程"的设计,都违反这条基线。这条规则是所有后续四条规则的前提——只要凭据仍然在 Agent 进程内,任何隔离方案都是形同虚设的。

第二条,凭据必须按任务周期撤销。任何凭据的使用必须限定在特定任务窗口内,任务结束立即撤销;不允许长期持有凭据、跨任务复用凭据、把凭据写进持久化存储。这条规则对应 macaroon 能力令牌的核心思想,即便不采用 macaroon,也必须在工程上做到凭据的短期化。

第三条,凭据替换必须经过可审计的中间层。Agent 和真实凭据之间必须有一层中间组件——可以是网关、代理、server 中间件、JS 执行环境;中间组件必须记录每一次替换的 Agent ID、时间戳、目标端点、调用结果。这条规则对应"可观测性"要求——事后必须能区分"Agent 自主行为"和"被劫持后的行为",区分的唯一可靠手段是中间组件的审计日志。

第四条,凭据替换策略必须在外部配置文件中定义,Agent 代码不能改写。任何"凭据替换按这个策略走"必须由治理侧在外部 YAML/JSON 文件里集中定义,Agent 代码不能修改这些策略;策略变更不需要重新部署 Agent。这条规则对应"治理侧的独立性"——Agent 是被治理的对象,不能成为治理的制定者。

第五条,任何凭据方案的失败必须有兜底。即便采用了上述所有四条,仍然会有凭据泄漏的可能——密钥管理服务被攻破、代理进程被劫持、server 中间件有 bug、Agent 自身被完全控制;兜底机制包括凭据的快速撤销、定期轮换、异常检测告警。这条规则对应"纵深防御"思想——单一防线总有被绕过的可能,多层防线叠加才能让真实事故代价可控。

从 MCP 凭据隔离推到企业 Agent 治理

把视野拉宽到企业 Agent 治理,凭据隔离的工程基线在更广义的 Agent 系统里同样成立。任何企业内部被授予"调用外部 API + 持有访问权限"的 Agent,都应当遵循上述五条规则——而不只是 MCP 协议下的 Agent。具体包括:被授权调用 CRM API 的销售 Agent、被授权调用财务系统 API 的财务 Agent、被授权调用客服工单系统的客服 Agent——它们每一个都面对同样的中毒概率,只是数据敏感度不同。

企业 Agent 治理负责人现在应当把 MCP 凭据隔离的工程基线作为整个企业 Agent 工具链凭据治理的样板工程。具体来说,"持有权/使用权解耦、按任务周期撤销、经过可审计中间层、外部策略不可改写、纵深防御兜底"这五条规则应当通用化,覆盖企业内部所有 Agent 的所有凭据使用场景,而不是只针对 MCP。每一条规则对应一个独立的工程组件——可以是 macaroon 网关、可以是 mitmproxy 代理、可以是 server 中间件、可以是 JS 执行沙箱,实现形式各异但原则一致。

更往前一步,这套凭据隔离基线还可以反过来影响新一代 Agent 工具的设计规范。新设计的 Agent 工具应当默认支持"凭据持有权/使用权解耦"——任何让 Agent 直接持有凭据的设计,在工程评审阶段就该被打回;应当默认支持"按任务周期撤销"——任何让 Agent 持有长期凭据的设计,在工程评审阶段就该被打回;应当默认支持"经过可审计中间层"——任何让 Agent 跳过中间层直接访问外部资源的设计,在工程评审阶段就该被打回。这三个"默认"加在一起,就是下一代 Agent 工具的事实标准。

企业 Agent 治理负责人现在必须回答的三个问题

面对上述基线,企业 Agent 治理负责人现在至少要直接回答三个问题。第一个问题:你企业内部现在有多少 Agent 直接持有长期静态凭据?每个 Agent 持有哪几个外部服务的凭据、凭据有效期是多久、谁能撤销这些凭据?这个问题不回答,任何"凭据隔离"都是事后追溯,不是事前治理。

第二个问题:你的 Agent 调用外部 API 时,中间经过了几层可审计的中间组件?如果答案是 0,凭据直接进 Agent,所有"治理"都是纸面文章;如果答案是 1+层,审计日志能覆盖所有中间层的所有调用吗?

第三个问题:你的 Agent 持有的任何访问权利,能不能在任意时刻被审计和撤销?如果答案是"按轮换周期自然撤销",事故的窗口期就是轮换周期;如果答案是"任意时刻撤销",任何一次策略更新都能立刻切断 Agent 的访问路径。

结语

Agent 的能力边界在过去一年被显著推前,从"按 prompt 调用一个外部 API"演化到"按 prompt 自主发现并调用多个外部 API"。每推前一步,工程侧的凭据治理基线就必须同步推前一步。把"长期静态凭据交给 Agent"作为主要工程模式的部署,在过去一年的多起 Agent 安全事故里被反复证明不够。把"凭据持有权和使用权解耦、凭据按任务周期撤销、替换经过可审计中间层、策略由外部集中管理、纵深防御兜底"作为基线,重新设计 Agent 的凭据体系,是当下能落地的工程基线。

任何企业 Agent 项目,只要涉及 Agent 调用外部 API 或持有访问权限,工程基线就必须做到这五条——持有权/使用权解耦、按任务周期撤销、经过可审计中间层、外部策略不可改写、纵深防御兜底——否则,今天不是某次凭据泄漏事故的主角,也会是下一次凭据泄漏事故的主角。