MCP Apps 默认安全设计:为什么"不声明即禁止"是嵌入AI聊天UI的终极答案?
【免费下载链接】ext-appsOfficial repo for spec & SDK of MCP Apps protocol - standard for UIs embedded AI chatbots, served by MCP servers项目地址: https://gitcode.com/GitHub_Trending/ex/ext-apps
MCP Apps 是 Model Context Protocol(MCP)的官方扩展协议与 SDK,让 MCP 服务器能够向 AI 聊天应用(如 Claude、ChatGPT)内嵌交互式 UI。它的核心安全理念是默认安全设计:不声明即禁止——应用每一点网络访问、每一项设备权限,都必须预先显式声明,否则一律被宿主(Host)拒绝。本文将带你快速理解这套连接策略的价值。
为什么"不声明即禁止"如此重要
当 AI 聊天客户端嵌入来自各个 MCP 服务器的 UI 时,它面对的其实是不可信的内容。官方规范明确列出了威胁模型:恶意服务器投递有害 HTML、被攻破的 UI 尝试逃逸沙箱、UI 未授权调用工具、UI 外泄宿主敏感数据、钓鱼与社会工程攻击。
应对方式不是"事后拦截",而是默认拒绝(deny by default):
- 未声明域名的网络请求 → 直接被 Content Security Policy(CSP)阻断
- 未声明的设备权限(摄像头、麦克风、地理位置)→ iframe 的
allow属性中不存在,浏览器层面就拒绝 - 未完成的 OAuth 授权 → 受保护工具直接返回
401,请求根本到不了业务逻辑
这种策略的价值在于:安全边界由声明决定,而非由开发者"记得写拦截代码"决定。漏写的代码是漏洞,漏写的声明只是功能缺失——后者远比前者安全。
四层安全架构:从规范中看实现
1. 强制 Iframe 沙箱
规范要求:所有 View 内容MUST(必须)渲染在带受限权限的沙箱 iframe 中,所有与宿主的通信都走postMessage且由宿主掌控。双 iframe 结构(Sandbox Proxy + 内层 iframe)进一步隔离了不可信 HTML。参考宿主实现可见于 examples/basic-host/src/sandbox.ts。
2. 默认全拒绝的 CSP 构建
这是"不声明即禁止"最直接的体现。规范中宿主构建 CSP 的模板以default-src 'none'开头,每一项资源都只有两类来源:'self'或你在元数据里显式声明的域名:
default-src 'none'; connect-src 'self' /* + connectDomains */ frame-src 'none' /* 除非声明 frameDomains */ object-src 'none';关键安全要求写在规范 Security Implications 一节(specification/2026-01-26/apps.mdx):
Host MUST block connections to undeclared domains—— 宿主必须阻断到未声明域名的连接。
也就是说,你的应用不填connectDomains,就一次外部请求也发不出去。
3. 设备权限同样"未声明即禁用"
permissions元数据映射到 iframe 的allow属性:只有声明了camera才推入camera,声明了microphone才允许麦克风。没写 = 没有权限,浏览器直接拒绝调用。
4. 可审计的双向通信
所有 View 到宿主的通信都走可审计的 MCP JSON-RPC 消息,宿主验证每一条入站消息、拒绝畸形类型、并可记录 UI 发起的 RPC 调用用于安全审查。通信层 SDK 见 src/app-bridge.ts。
实操指南:如何声明应用需要的连接域名
如果你的 MCP App 需要调用外部 API,在 UI 资源的_meta.ui.csp中声明即可(详见 docs/csp-cors.md):
_meta: { ui: { csp: { connectDomains: ["https://api.example.com"], // fetch/XHR/WebSocket }, domain: APP_DOMAIN, // 可选:给 API 服务器的稳定 Origin,用于 CORS 白名单 }, }两个容易踩的坑:
- 开发环境的
localhost也要声明——本地调试时同样适用"不声明即禁止" - 公网 API 若返回
Access-Control-Allow-Origin: *或使用 API Key 认证,则无需配置domain;只有需要 CORS 白名单的 API 才需要稳定 Origin
完整配置示例可参考 examples/sheet-music-server 与 examples/map-server。
进阶:用 OAuth 为工具加锁
"不声明即禁止"不止用于网络,也延伸到授权。MCP Apps 支持两种模式(详见 docs/authorization.md):
| 模式 | 行为 | 适用场景 |
|---|---|---|
| 按服务器授权 | 连接即校验,所有请求必须携带有效 Token | 全部工具都敏感 |
| 按工具授权 | 仅受保护工具触发 OAuth 流程,公共工具直接放行 | 公共/敏感工具混合 |
更精妙的模式是UI 发起的授权升级:应用先以公共数据加载(无登录墙),用户点击敏感操作时,宿主才透明地完成 OAuth 流程并带 Token 重试。体验快,安全边界清晰。
如何验证:端到端安全测试
仓库内置了 Playwright 安全测试 tests/e2e/security.spec.ts,对真实运行的示例服务器验证沙箱与权限行为,可作为你自己宿主实现的安全基线参考。
总结:默认拒绝带来的三重收益
- 对宿主开发者:安全逻辑收敛为一条规则——"元数据没写的,一律拒绝",无需为每个应用写拦截代码
- 对应用开发者:安全声明集中在资源元数据一处,声明即文档,边界一目了然
- 对最终用户:嵌入在 AI 对话中的 UI 被限制在一个最小权限的沙箱里,即使服务器被攻破,攻击面也被 CSP + 沙箱 + 权限白名单层层封死
想动手体验?按 docs/quickstart.md 克隆仓库后执行npm install && npm start(仓库地址:git clone https://gitcode.com/GitHub_Trending/ex/ext-apps),打开 http://localhost:8080/ 即可在参考宿主中浏览全部示例。
【免费下载链接】ext-appsOfficial repo for spec & SDK of MCP Apps protocol - standard for UIs embedded AI chatbots, served by MCP servers项目地址: https://gitcode.com/GitHub_Trending/ex/ext-apps
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考