结论先说:MCP 的安全边界在客户端,不在协议
MCP 把「任意数据访问」和「代码执行路径」同时交给了模型,而协议本身无法在协议层强制安全——官方规范写得很直白:安全原则要由实现方(也就是客户端)来落地。所以防提示注入、防数据外传、防恶意 URL 的责任,全在集成方这一侧。
把 MCP 当成一个「插件市场」去装,是当下最大的风险来源。正确姿势是把它当成一个需要自己设防的执行边界。
四类攻击面与对应防线
| 攻击面 | 典型手法 | 防线 |
|---|---|---|
| 工具描述投毒 | 在 description 里藏指令,诱导模型做额外动作 | 把工具描述视为不可信输入,除非来自受信任服务端 |
| 工具结果注入 | 返回内容里夹带指令,劫持后续推理 | 客户端在把结果喂给模型前做校验与标注 |
| OAuth URL 投毒 | 返回 javascript: 等危险协议的授权地址 | 只允许 http/https,禁止用 shell 打开 URL |
| 越权访问与外传 | 读写工作目录之外的文件、向外部域名发数据 | 文件系统 + 网络双重隔离 |
官方规范里最关键的一条:工具的行为描述(包括注解)应当被视为不可信,除非来自受信任的服务端。这一条决定了整个威胁模型——你不能假设一个第三方 MCP 服务的自我介绍是诚实的。
客户端侧必须做的六件事
- 连接前展示完整命令:配置本地服务时,把将要执行的命令(含全部参数)完整、不截断地展示给用户,并明确标注这是会在本机执行代码的高风险操作,必须显式确认。
- 校验授权 URL 协议:只接受 http/https,其中 http 仅允许回环地址用于本地开发;明确拒绝 javascript:、 data:、 file:、 vbscript: 等协议,且用白名单而非黑名单做校验。
- 禁止用 shell 打开 URL:不要用 cmd 、 PowerShell 或 shell 脚本去打开授权链接,改用平台原生的非 shell 打开方式,否则 URL 里的特殊字符会被解释成额外命令。
- 沙箱化运行服务:让 MCP 服务运行在最小权限的沙箱里,限制文件系统、网络和系统资源访问,并提供按需提权的机制。
- 工具调用前给用户确认:任何工具调用都要有人工在环,UI 必须清楚展示正在调用哪个工具、传了什么参数,防止恶意或意外的数据外传。
- 记录与限流:记录每一次工具调用用于审计,对调用频率做限流,并对可疑的授权 URL 打日志。
被低估的一条:stdio 不是沙箱
很多人的直觉是「用 stdio 就安全」,这是误解。 stdio 传输本身没有漏洞,但客户端把服务端作为子进程拉起时,双方运行在同一环境权限下:一个恶意的服务端本来就获得了任意代码执行能力,一个恶意的客户端也完全控制了它拉起的服务端。也就是说,stdio 通道两端互为完全信任。
更危险的是代理架构:如果有一个独立的代理服务负责管理 stdio 连接、并可以派生 MCP 服务子进程,那么一个 Web 侧的攻击就能顺着这条路径升级为完整的系统沦陷。
上线前的自查清单
- 是否完整展示了将要执行的命令(含参数)?
- 是否校验了授权 URL 的协议白名单?
- 是否存在用 shell 打开 URL 的代码路径?
- 服务是否运行在受限沙箱中,且凭据不在沙箱内?
- 工具调用是否默认需要用户确认?
- 敏感操作(写文件、发网络请求)是否有二次确认?
- 是否记录了可审计的调用日志?
三条实践原则
- 凭据不进沙箱:这是最有效的一条。凭据不在沙箱里,无论攻击来自用户、模型还是外部攻击者,都偷不走。
- 文件系统隔离与网络隔离必须同时做:只有网络隔离,被攻陷的 Agent 仍可能通过文件系统逃逸后再联网;只有文件系统隔离,它仍能直接把敏感文件发出去。
- 权限收紧后可放宽审批:边界足够硬时,反而可以减少逐个动作的确认弹窗,既安全又流畅。这是沙箱真正的价值——它换来的是自动化空间。
MCP 协议本身能防提示注入吗?
不能。协议层无法强制这些安全原则,官方明确要求由实现方构建同意与授权流程、访问控制和数据保护。
工具描述可以信任吗?
默认不可信。规范建议:除非来自受信任的服务端,否则工具注解应视为不可信输入。
stdio 传输比 HTTP 安全吗?
场景不同。 stdio 不做认证,两端同权限运行,谁被攻陷另一方都完蛋;远程 HTTP 则必须要求授权令牌。两者都需要额外隔离。
用户批准提示会不会变成走过场?
会。大量重复弹窗会导致审批疲劳,用户开始无意识地点击同意。解法是用沙箱把边界收紧,减少需要确认的次数,而不是靠弹窗本身。









评论 (0)