每个MCP服务器都是您的代理可能泄露的又一个密钥。
每个MCP服务器都意味着又一个正在运行的秘密,进而扩大了恶意输入所能影响的范围。应对令牌的作用域进行审计,并应用四项修复措施以缩小其破坏范围。
来源: Every MCP Server You Add Gives Your Agent Another Credential
一切起初都很简单。周一连接了 GitHub,周二连接了 Slack,接着是 Postgres,然后是 Notion,还有同事在某个下午编写但从未记录过的内部计费服务器。每一次新增都让这个自动化工具的功能更加强大,同时也为它携带的“密钥串”增添了一把钥匙。
问题往往是在偶然情况下暴露的——当有人正在调试其他无关问题时打开了配置文件。六台服务器,六组敏感信息,全都以明文形式存在于个人目录中,既没有过期时间也没有所有者。到了那时,MCP就不再显得是个方便的工具,反而变成了一个隐患。
数据统计
在几个月的热情使用之后,典型的配置情况如下:
{
"mcpServers": {
"gh": { "cmd": "gh-mcp", "env": { "GH_TOKEN": "ghp_live_9f2a" } },
"db": { "cmd": "pg-mcp", "env": { "PG_URL": "postgres://app:hunter2@prod/main" } },
"sl": { "cmd": "slk-mcp", "env": { "SLACK_TOKEN": "xoxb-live-771" } }
}
}
三台服务器,三个密钥,一个文件,且不进行轮换。只需在常见的配置文件夹中搜索类似令牌的字符串,几秒钟内就能检查自己的机器。
grep -rIl "ghp_\|xoxb-\|sk-" ~/.config ~/.claude ~/Library 2>/dev/null | wc -l
运行它的人往往能得到超出预期的结果。原本以为只有三个,实际却有十一个,这种情况并不少见。
真正棘手的部分
存储在磁盘上的凭证只是问题中较为温和的一面;更严重的是,每一个凭证都由唯一的决策者掌控,而该决策者还需从互联网上获取相关文本。
one agent loop
|
+---------+---------+---------+---------+
| | | | |
github slack postgres notion billing
(14 scp) (9 scp) (rw all) (rw ws) (rw all)
| | | | |
+---------+---------+---------+---------+
|
one poisoned input
问题评论、Notion 页面或表格中的某一行都可能提出相应的行动建议,而处理该任务的智能体拥有执行这些行动所需的所有关键信息。这其实就是“伪装起来的混乱问题”。该智能体并未受到损害,它只是在提供帮助,却无法区分用户的指令与那些仅表面相似的文本。
衡量差距
一个有效的做法是记录两周内所有的工具调用情况,然后对比每个标记被允许执行的操作与它实际执行的操作。某次此类审计的结果如下:
| Service | Granted Scopes | Scopes Used | Utilization |
| -------- | --------------: | ------------------: | ----------: |
| GitHub | 14 | 3 | 21.4% |
| Slack | 9 | 2 | 22.2% |
| Postgres | Full RW | 1 table, reads only | Minimal |
| Notion | Whole workspace | 2 pages | Minimal |
大约五分之四的授权访问权限从未被使用。这种状况并非出于便利考虑,而是意味着你承担了风险却得不到任何回报。
按收益排序的解决方案
每台服务器应拥有一个专用令牌,且该令牌仅用于该服务器的实际功能。共享令牌意味着责任共担;一旦出现问题,审计日志应指向服务器而非个人。
所有存储数据的对象默认都应设置为只读模式。只需两行SQL代码、一个专用角色,以及为代理程序实际需要读取的表设置相应的读取权限,就能将最糟糕的情况从表数据丢失转变为查询信息泄露。
create role agent login password 'rotate_me';
grant usage on schema public to agent;
grant select on orders, users to agent;
用短期令牌替代永久令牌。让保险库按需发放有效期仅为几分钟的凭证。周二上午被盗的密钥,到周二下午就已毫无价值。
async function tok(srv) {
const r = await fetch(`${VAULT}/issue/${srv}`, { method: "POST" });
const { token, ttl } = await r.json();
return { token, exp: Date.now() + ttl * 1000 };
}
不仅要记录聊天内容,还要记录通话内容。为每个工具函数添加一个简单的封装,用于记录服务器信息、参数以及时间戳,只需几行代码即可实现。在上述审计中,这就暴露出了服务器调用了本不应访问的端点。
function wrap(name, fn) {
return async (args) => {
log({ srv: name, args, at: Date.now() });
return fn(args);
};
}
应遵循的规则
在引入新服务器之前,先假设它会具有攻击性,并思考在没有帮助的情况下它能获取什么。如果诚实的答案令人不安,那就缩小其权限范围或直接不引入该服务器。切勿承诺日后再解决这个问题,因为“日后”往往不会到来,尤其是在周五。
以上内容并非反对MCP。它确实解决了曾让智能体显得像玩具一样的集成混乱问题。关键在于,每添加一台服务器就会增加一个凭证,而这些凭证应当得到与生产环境中的其他密钥相同的重视程度。
相关阅读
- 实用指南:使用 EODHD 的 MCP Server 构建 AI 金融智能体 — 该指南的详细操作步骤:包括合同模板、检查清单,以及为无需自行实现 MCP 系统的团队准备的即用代码模块。
- 实用指南:您的智能体能否搜索 MCPs — 该指南的详细操作步骤:包括合同模板、检查清单,以及为采用该模式的团队准备的即用代码模块。