跳转到主要内容

安全与认证

7.1 MCP安全模型

MCP的安全责任分布在不同层面:
层面负责方内容
传输安全传输层HTTPS加密、证书验证
身份认证OAuth 2.1 + Server验证调用方身份
权限控制Server控制不同用户的工具访问权限
数据隐私Server + Host不泄露敏感数据给AI模型
工具安全Host高风险工具调用需用户确认

7.2 OAuth 2.1认证

MCP规范基于 OAuth 2.1(draft-ietf-oauth-v2-1-13)定义远程Server的认证机制。这不是MCP发明的新认证方案,而是完全基于现有OAuth标准和相关RFC规范。

涉及的RFC标准

RFC名称在MCP中的作用
draft-ietf-oauth-v2-1-13OAuth 2.1核心授权框架
RFC 7636 (PKCE)Proof Key for Code Exchange防止授权码拦截攻击(MCP强制要求S256方法)
RFC 8414Authorization Server MetadataClient自动发现授权服务器的端点和能力
RFC 7591Dynamic Client RegistrationClient首次连接时自动注册
RFC 9728Protected Resource MetadataServer声明自己的认证要求和关联的授权服务器
RFC 8707Resource IndicatorsClient指定请求token的目标资源

完整认证流程

Step 1: Client向MCP Server端点发送请求
        → Server返回401 Unauthorized

Step 2: Client获取Protected Resource Metadata (RFC 9728)
        GET https://mcp-server.example.com/.well-known/oauth-protected-resource
        → 获得authorization_servers列表

Step 3: Client获取Authorization Server Metadata (RFC 8414)
        GET https://auth.example.com/.well-known/oauth-authorization-server
        → 获得authorization_endpoint, token_endpoint等

Step 4: Dynamic Client Registration (RFC 7591, 如需)
        POST https://auth.example.com/register
        → 获得client_id(无client_secret,因为是公共客户端)

Step 5: Authorization Code + PKCE (S256)
        a. Client生成code_verifier和code_challenge
        b. 引导用户访问authorization_endpoint
        c. 用户授权后获得authorization_code
        d. Client用code + code_verifier换取access_token

Step 6: 后续请求携带Bearer Token
        POST https://mcp-server.example.com/mcp
        Authorization: Bearer eyJhbG...
        Mcp-Session-Id: session-abc123

PKCE (Proof Key for Code Exchange)

MCP强制要求PKCE,且必须使用 S256 方法(不允许 plain)。PKCE防止授权码被中间人拦截后滥用:
// 1. Client生成随机code_verifier
code_verifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"

// 2. 计算code_challenge = BASE64URL(SHA256(code_verifier))
code_challenge = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"

// 3. 授权请求携带code_challenge
GET /authorize?
  response_type=code&
  client_id=my-client&
  code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&
  code_challenge_method=S256&
  redirect_uri=http://localhost:8080/callback

// 4. 换取token时携带code_verifier
POST /token
  grant_type=authorization_code&
  code=AUTH_CODE&
  code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

Token类型

MCP使用 Bearer Token。access_token在HTTP请求头中传递:
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...

7.3 Server端认证实现

import express from "express";

const app = express();

// OAuth token验证中间件
async function authenticate(req, res, next) {
  const token = req.headers.authorization?.replace("Bearer ", "");
  if (!token) {
    // 返回401,引导Client进行OAuth认证
    return res.status(401).json({
      error: "unauthorized"
    });
  }

  // 验证token(调用授权服务器的introspection端点或本地JWT验证)
  const user = await verifyToken(token);
  if (!user) return res.status(403).json({ error: "invalid_token" });

  req.user = user;
  next();
}

// Protected Resource Metadata端点 (RFC 9728)
app.get("/.well-known/oauth-protected-resource", (req, res) => {
  res.json({
    resource: "https://mcp-server.example.com",
    authorization_servers: ["https://auth.example.com"],
    bearer_methods_supported: ["header"]
  });
});

// MCP端点(需认证)
app.post("/mcp", authenticate, async (req, res) => {
  const result = await handleMcpRequest(req.body, req.user);
  res.json(result);
});

7.4 权限控制

不同工具需要不同权限级别:
// 工具执行时检查权限
server.tool("create_return", "发起退货", schema, async (args, context) => {
  // 检查用户是否有权操作这个订单
  const order = await db.orders.findById(args.orderId);
  if (order.userId !== context.user.id) {
    return {
      content: [{ type: "text", text: "无权操作此订单" }],
      isError: true
    };
  }
  // 执行退货逻辑
  const returnRequest = await db.returns.create({
    orderId: args.orderId,
    reason: args.reason,
    userId: context.user.id
  });
  return {
    content: [{
      type: "text",
      text: `退货申请已创建: ${returnRequest.id}`
    }]
  };
});

工具级权限矩阵

建议为每个工具定义明确的权限要求:
权限级别说明示例工具
公开无需认证search_products, get_categories
登录需要有效的access_tokenget_order, list_orders
所有者只能操作自己的数据create_return, update_profile
管理员需要管理员权限update_inventory, manage_promotions

7.5 数据隐私

原则: Server返回的数据会被AI模型处理。不要在工具返回中包含不应该被AI”看到”的敏感信息。
可以返回不应返回
商品名、价格、库存用户密码、支付凭证
订单状态、物流信息完整信用卡号
公开的公司信息内部成本价、利润率
脱敏后的用户信息完整身份证号

Resource的Annotations辅助隐私控制

通过Annotations的 audience 字段控制资源的目标受众:
{
  "uri": "commerce://analytics/daily",
  "annotations": {
    "audience": ["assistant"],
    "priority": 0.3
  }
}
audience: ["assistant"] 表示该资源仅供AI模型处理,不应直接展示给用户。Host可以据此决定是否在UI中隐藏该资源。

7.6 传输安全

远程Server(Streamable HTTP)

  • 强制HTTPS: 生产环境必须使用TLS加密
  • 证书验证: Client应验证Server的TLS证书
  • CORS: 如果Client是浏览器应用,Server需配置适当的CORS策略

本地Server(stdio)

stdio本地Server天然安全(不暴露网络),但仍需注意:
  • 不要硬编码数据库密码在代码中,用环境变量
  • 限制文件系统访问范围(配合Roots机制)
  • 日志不要记录敏感数据
  • Server进程以最小权限运行

7.7 安全检查清单

开发MCP Server时,对照以下清单确保安全:
  • 远程Server是否使用HTTPS
  • 是否实现了OAuth 2.1认证(含PKCE S256)
  • 敏感工具是否检查了用户权限
  • 返回数据是否经过脱敏处理
  • 环境变量是否用于存储密钥
  • 日志中是否排除了敏感信息
  • 是否对输入参数进行了验证和清理
  • 是否实现了请求频率限制

下一章: 测试与调试 — MCP Inspector使用指南