MCP如何鉴权?

问题概述

当 MCP‑Server 采用 ​SSE 模式(transport="sse")独立后台运行​,默认情况任何人知道 http://ip:8001/sse 地址就可以连接并调用所有工具(RAG 检索、查订单、创建工单),存在严重安全隐患。

针对 Fast‑MCP 的鉴权方案分 4 种,由轻到重:

  1. 请求头 Token 鉴权(简单,适合内网);
  2. API‑Key + HTTPS(外网生产推荐);
  3. OAuth2 / JWT(对接用户登录系统);
  4. 反向代理(Nginx 鉴权,业务代码无侵入)。

注意:原生fast‑mcp内置的sse模式​本身没有提供鉴权参数​,底层是基于 Starlette 框架;我们有两种实现方式:

  • 方案 A:自定义 Starlette 中间件拦截请求(推荐,代码内实现);
  • 方案 B:Nginx 反向代理做鉴权(生产最佳实践,解耦业务代码)。

环境依赖

pip install fast‑mcp python‑multipart python‑jose passlib

方案 1:自定义中间件,Header 携带 API‑KEY(代码层面鉴权,最常用)

原理:客户端发起 SSE 连接时,在请求头携带 X‑MCP‑Api‑Key,服务端中间件校验密钥,密钥不对直接拒绝连接。

mcp_server/server.py(修改 MCP‑Server 源码)

from fastmcp import FastMCP
from starlette.applications import Starlette
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from vector_db import search_kb

# 全局密钥,生产环境放到环境变量,不要硬编码
VALID_API_KEY = "sk‑mcp‑123456789"

mcp = FastMCP("knowledge‑server")

@mcp.tool(description="查询售后知识库")
def rag_search(query: str) -> str:
    return search_kb(query)

# 鉴权中间件
class AuthMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        # 只对sse接口鉴权
        if request.url.path == "/sse":
            client_key = request.headers.get("X‑MCP‑Api‑Key")
            if client_key != VALID_API_KEY:
                from starlette.responses import PlainTextResponse
                return PlainTextResponse("Unauthorized", status_code=401)
        response = await call_next(request)
        return response

if __name__ == "__main__":
    # 获取底层starlette实例,挂载中间件
    app: Starlette = mcp.sse_app()
    app.add_middleware(AuthMiddleware)
    app.run(host="0.0.0.0", port=8001)

LangChain‑MCP‑Client 携带 Header

langchain‑mcp‑adaptersMultiServerMCPClient可以通过headers参数配置请求头:

client = MultiServerMCPClient({
    "rag_server": {
        "url": "http://127.0.0.1:8001/sse",
        "transport": "sse",
        "headers": {
            "X‑MCP‑Api‑Key": "sk‑mcp‑123456789"
        }
    }
})
  • 密钥错误,会抛出 401 拒绝建立 SSE 连接,完全不能调用工具。
  • 优化:生产环境将VALID_API_KEY从环境变量读取,不要写死代码:os.getenv("MCP_API_KEY")

方案 2:Nginx 反向代理鉴权(生产环境首选,推荐)

不修改 Python 代码,把鉴权交给 Nginx;MCP‑Server 只监听127.0.0.1:8001,外部网络访问不到服务,Nginx 对外开放 443 并且强制 HTTPS。

  1. MCP‑Server 仅监听本机:mcp.run(sse, host="127.0.0.1", port=8001)
  2. Nginx 配置 HTTPS 证书;
  3. Nginx 校验请求头X‑MCP‑Api‑Key;密钥不对直接 401;
  4. 并且配置 IP 白名单,只允许你的后端服务 IP 访问。
server {
    listen 443 ssl;
    server_name mcp‑api.xxx.com;
    ssl_certificate cert/xxx.pem;
    ssl_certificate_key cert/xxx.key;

    set $api_key "";
    if ($http_x_mcp_api_key != "sk‑mcp‑123456789"){
        return 401;
    }
    # IP白名单,只允许你的服务机器
    allow 你的agent服务器IP;
    deny all;

    location /sse {
        proxy_pass http://127.0.0.1:8001;
        proxy_http_version 1.1;
        proxy_set_header Connection '';
        proxy_buffering off;
    }
}

优点:

  1. Python 代码干净;
  2. 强制 HTTPS,防止密钥抓包;
  3. IP 白名单 + HTTPS + API‑Key 三重防护;
  4. 可以配置限流、防高频调用。

生产环境必须开启 HTTPS,如果是 HTTP,header 里面的 API‑Key 会明文传输,密钥会被窃取。


方案 3:JWT 鉴权(适用于多租户场景)

当你的 MCP 服务给多个业务方使用时,用 JWT 代替固定 API‑Key:

  1. Agent 服务先向认证服务获取短期 JWT 令牌(有效期 1‑2 小时);
  2. Client 请求头:Authorization: Bearer xxx
  3. MCP‑Server 中间件解析 JWT,验证签名是否合法,并且解析租户 ID;
  4. 根据租户 ID 隔离知识库,实现多租户 RAG。

服务端解析 JWT 示例片段:

from jose import jwt
SECRET_KEY = os.getenv("JWT_SECRET")
payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
user_id = payload["sub"]

方案 4:stdio 模式(完全不暴露端口,开发环境最安全)

如果你只有 LangChain Agent 调用 MCP‑Server,​优先使用 stdio 模式​:

  • MCP‑Server 不会开启任何网络端口;
  • Agent 通过子进程启动 MCP‑Server,通信是进程内管道;外部网络完全无法访问 MCP‑Server,不存在被外部调用风险;
    缺点:只能同一台机器部署;Agent 和 MCP‑Server 不能分开部署在不同服务器。

5. 进阶安全加固要点(落地必做)

(1)网络层面

  1. SSE 服务只监听127.0.0.1,由 Nginx 代理暴露;
  2. 安全组:云服务器防火墙关闭 8001 端口,仅开放 443;
  3. 严格配置 IP 白名单,只有 Agent 服务能访问 MCP 服务。

(2)应用层面

  1. API‑Key 设置足够复杂,定期轮换;不要硬编码在代码,统一环境变量或者配置中心;
  2. 开启 HTTPS,禁止 HTTP 访问;
  3. 给 MCP 工具增加权限控制:例如:
    • Agent‑A 只能调用rag_search
    • Agent‑B 才可以调用查询订单的工具;
      在中间件解析 token 之后,动态过滤可用工具列表;
  4. 请求限流:Nginx 或者 Starlette‑limiter 限制每秒调用次数,防止被刷向量服务;
  5. 日志审计:每次调用 MCP 工具记录调用方标识、入参,方便排查安全问题。

(3)容器部署注意事项

Docker 部署 MCP‑Server 时:

  • 容器内端口 8001 不映射到宿主机;
  • 只有同一 docker‑network 里 Agent 容器能够访问 MCP‑Server。

选型建议总结

  1. 开发环境:stdio 模式,无需鉴权,最安全​;
  2. 内网分离部署:Header+API‑Key 中间件鉴权即可​;
  3. 公网环境(生产强烈推荐):Nginx + HTTPS + API‑Key + IP 白名单​;
  4. 多租户平台:采用 JWT 鉴权 + 租户隔离知识库​。

评论 (0)

暂无评论