当大型语言模型需要向用户展示交互式数据可视化、复杂表单或实时仪表盘时,传统的文本回复已难以满足需求。MCP Apps(Model Context Protocol Apps)作为 MCP 协议的扩展,允许 MCP 服务器返回可交互的 HTML 界面,这些界面直接渲染在 Claude 等主机的对话上下文中。本文不从协议规范入手,而是聚焦于这一架构背后的运行时安全机制,探讨沙箱隔离如何防止第三方应用逃逸,以及工具调用边界如何设计才能在保持功能的同时规避风险。
沙箱隔离:iframe 渲染层的安全基石
MCP Apps 最核心的安全承诺来自于沙箱 iframe 渲染层。当 MCP 主机(如 Claude Desktop 或 Claude Web)调用一个声明了 _meta.ui.resourceUri 的工具时,主机首先通过 ui:// 协议获取对应的 HTML 资源,然后将其嵌入到一个受限的 iframe 中进行渲染。这一设计借鉴了现代浏览器对不可信内容的隔离思路,但针对 MCP 场景做了定制化约束。
从浏览器安全模型来看,沙箱 iframe 通过 sandbox 属性可以施加多重限制。默认情况下,沙箱会阻止弹出窗口、插件执行、表单提交以及脚本执行。更关键的是,当 iframe 没有携带 allow-same-origin 标记时,浏览器会将其视为来自「不透明源」(opaque origin),这意味着即使 iframe 内的代码尝试访问 localStorage、document.cookie 或执行跨域请求,也会因为同源策略检查失败而遭到拦截。对于 MCP Apps 而言,这种不透明源处理尤为重要 —— 它确保了即便 MCP 服务器返回的 HTML 中包含恶意脚本,该脚本也无法读取主机页面的会话信息或 DOM 结构。
MCP 协议进一步通过 Content Security Policy(CSP)声明来收紧外部资源加载。工具描述中的 _meta.ui.csp 字段允许 MCP 服务器声明该应用可以加载外部脚本或资源的白名单域名,而主机在渲染时会强制执行这些 CSP 规则。例如,一个地图可视化应用可能需要从 Mapbox 或 Google Maps 加载地图瓦片,它会在 CSP 中声明这些域名;而主机则会阻止该应用向任何未列出的域名发起请求。这种声明式控制将资源加载的决策权交给应用作者,同时保留了主机对安全边界的最终控制权。
postMessage 通信层:双向数据流的安全通道
iframe 隔离解决了渲染层面的问题,但 MCP Apps 的设计目标之一是实现主机与应用之间的双向数据流 —— 应用需要调用 MCP 工具获取实时数据,主机需要向应用推送工具执行结果。这一交互通过 postMessage API 实现,但 MCP 在其基础上定义了一套基于 JSON-RPC 的应用专属协议,并将其命名为 App Bridge 的一部分。
postMessage 的设计本身要求严格的来源验证。主机在接收来自 iframe 的消息时,必须检查 event.origin 是否在允许的来源列表中。然而,由于沙箱 iframe 默认使用不透明源,event.origin 不会是一个可验证的域名,这意味着主机无法依赖浏览器的同源机制来验证消息来源。MCP 的解决方案是在应用初始化阶段通过消息体中的握手信息建立信任关系,并在后续所有消息中携带会话标识符或令牌,以便主机在应用层进行验证。
在消息内容层面,MCP Apps 协议对可传递的字段做了明确约束。应用可以发送的消息类型包括 ui/callTool(请求调用工具)、ui/log(发送日志)、ui/updateContext(更新模型上下文)等,而主机返回的消息类型包括 ui/initialize、ui/toolResult、ui/error 等。这种模式化的消息类型设计确保了通信内容的可预期性,主机可以基于类型白名单过滤非法消息。例如,如果应用尝试发送一个未在协议中定义的 ui/execScript 消息,主机应当直接拒绝处理并可能终止该会话。
工具调用边界:主机端的权限控制策略
沙箱隔离和消息通道是底层保障,但 MCP Apps 的安全模型中最灵活也最关键的环节在于工具调用边界的控制。MCP 服务器通常注册了大量工具供 LLM 调用,这些工具可能涉及文件系统访问、数据库查询、API 调用甚至代码执行。当一个 MCP App 运行时,它是否应该被允许调用所有这些工具?答案显然是否定的 —— 一个简单的天气查询应用显然不需要访问用户的代码仓库。
主机端实现通常采用工具调用代理模式,即应用发出的 ui/callTool 请求不会直接到达 MCP 服务器,而是先经过主机的权限检查层。主机维护着一套工具调用策略,该策略可能基于以下维度进行决策:工具名称白名单(仅允许调用特定工具)、参数模式验证(检查参数是否符合预期 Schema)、调用频率限制(防止应用通过高频调用进行资源耗尽或数据爬取),以及敏感操作二次确认(对于删除文件、发送邮件等操作要求用户显式授权)。
以 Claude 的实现为例,当 MCP App 尝试调用一个被标记为「敏感」的工具时,主机会在发送请求前向用户展示确认对话框,说明应用正在请求什么权限以及为什么需要该权限。用户可以选择允许、拒绝或仅允许本次调用。这种用户介入的设计将最终的安全决策权交还给人类用户,同时也提高了用户对应用行为的感知度。
值得注意的是,MCP Apps 的安全模型假设主机是可信的执行环境。沙箱只能保护主机免受恶意应用的侵害,但不能保护用户免受恶意主机的侵害 —— 如果主机本身被攻陷或有意收集用户数据,沙箱机制无能为力。因此,企业在部署 MCP Apps 时,还需要关注 MCP 主机的来源可信度、审计日志能力以及数据驻留合规性等更高层面的因素。
威胁模型与工程实践建议
理解 MCP Apps 的安全边界后,我们可以勾勒出一个典型的威胁模型。潜在攻击者可能尝试以下几种攻击路径:通过恶意 HTML 注入窃取主机页面的会话 Cookie;利用 XSS 漏洞通过 postMessage 发送构造的消息来调用未授权的工具;通过长时间运行的 App 进行资源耗尽攻击;或者利用 CSP 配置不当加载外部恶意脚本。
针对这些威胁,工程实践中有几条关键建议。首先,在主机端实现中,应始终对 postMessage 消息进行严格的内容模式验证,拒绝缺少必需字段或包含异常字段的消息。其次,CSP 配置应遵循最小权限原则,仅允许应用真正需要的外部来源,并避免使用 unsafe-inline 或 unsafe-eval 等指令。第三,对于工具调用代理层,应记录完整的调用审计日志,包括应用标识、请求的工具名称、参数摘要、请求时间以及用户确认状态,这些日志对于事后溯源和安全审计至关重要。第四,考虑为长时间运行的 App 实现超时机制,自动销毁超过一定时间未活跃的会话,释放沙箱资源并降低攻击窗口。
MCP Apps 代表了一种将交互式能力安全地嵌入 AI 对话中的工程范式。通过沙箱 iframe 隔离渲染层、postMessage 抽象通信层、主机端工具调用代理层这三重防线,它在保持应用功能的同时,将不可信代码的破坏力控制在有限范围内。对于正在构建 MCP 客户端或服务器的团队而言,深入理解这一安全模型并正确实现各层防护,是构建可信 AI 应用生态的基础。