如何构建一个安全的MCP服务框架

2 阅读4分钟

一、FastMCP开源项目

fastMCP(开源项目 modelcontextprotocol/fastmcp)是由 MCP 协议社区推动的一个官方参考实现(截至 2025–2026 年逐渐成熟)。但在安全方面,仍然有待加强。

用它来创建一个MCP SERVER非常简单。

from fastmcp import create_app, tool 
@tool 
def get_weather(city: str) -> str: 
    return f"Weather in {city} is sunny." 
   
app = create_app(tools=[get_weather])

通过tool装饰器可以实现MCP的tool功能,通过create_app建立MCP服务。

二、安全属性

但对于实际生产使用,还要考虑各种安全性,这在开源的fastMCP中并未实现。

根据2026年1月份版本的情况,在认证、授权、输入验证、速率限制、审计日志、敏感信息保护等方面还有待加强。

安全特性是否内置说明
认证(AuthN)❌ 否无 API Key、JWT、OAuth 支持
授权(AuthZ)❌ 否所有工具对所有调用者开放
输入验证⚠️ 部分依赖 Python 类型注解,但无运行时 schema 校验(易被绕过)
速率限制❌ 否无内置限流
审计日志❌ 否仅 debug 日志,无结构化审计
敏感信息保护❌ 否参数直接打印或返回

三、加强安全属性

接下来我们针对每一项,进行安全加强,使得更能满足生产需要。

1、认证

# ===== 认证 =====
VALID_API_KEYS = set(os.getenv("MCP_API_KEYS", "dev-key").split(","))

async def verify_api_key(x_api_key: str = Header(...)):
    if x_api_key not in VALID_API_KEYS:
       raise HTTPException(status_code=401, detail="Invalid X-API-Key")
    return x_api_key

通过设定并验证输入KEY的合法性,实现访问认证。

2、授权

原生的tool不支持role功能,可以对其进行扩展。

from fastmcp import tool as base_tool 
def secure_tool(roles: list = None): 
    def decorator(func): 
        func._mcp_roles = roles or ["user"] 
        return base_tool(func) 
    return decorator 
# 使用 
@secure_tool(roles=["admin"]) 
def delete_user(user_id: str): ...

然后在endpoint中添加对角色的判断

# 在 secured_endpoint 中 
tool_name = request.json().get("name") 
tool = server.tools[tool_name] 
required_roles = getattr(tool.func, "_mcp_roles", ["user"]) 
caller_role = get_role_from_api_key(api_key) 
if caller_role not in required_roles: 
    raise HTTPException(403, "Forbidden")

3、输入验证

为每个tool定义 Pydantic 模型,避免 LLM 注入任意参数。

from pydantic import BaseModel 
class SayHelloArgs(BaseModel): 
    name: str 

@register_tool("say_hello", roles=["user"]) 
def say_hello(args: SayHelloArgs): # ← 接收模型实例 return f"Hello, {args.name}!"

4、限流

这里引入slowapi实现限流限速。

from slowapi import Limiter  
from slowapi.middleware import SlowAPIMiddleware
limiter = Limiter(key_func=get_remote_address)

应用到具体接口

from security.rate_limit import limiter 
app.state.limiter = limiter 
@app.post("/mcp/v1/tool_call") 
@limiter.limit("10/minute") # 每 IP 10 次/分钟 
async def call_tool(...): ...

5、审计日志及自动脱敏

引入行为审计,并根据敏感列表对敏感字段进行脱敏。

import logging  
import json  
import time  

#敏感列表
SENSITIVE_PARAMS = {"password", "token", "secret", "key"}   

#脱敏操作
def sanitize(args: dict) -> dict:  
    return {k: "[REDACTED]" if k in SENSITIVE_PARAMS else v for k, v in args.items()}  
  
def log_call(api_key: str, ip: str, tool: str, args: dict, success: bool, msg: str, duration: float):  
    safe_args = sanitize(args)  
    record = {  
        "timestamp": time.time(),  
        "api_key_prefix": api_key[:6] + "...",  
        "client_ip": ip,  
        "tool": tool,  
        "arguments": safe_args,  
        "success": success,  
        "duration_sec": round(duration, 3)  
    }  
    if success:  
        record["result"] = msg  
    else:  
        record["error"] = msg  
    logging.info(json.dumps(record))

在call_tool中调用

start = time.time()  
try:  
    # ... 执行工具 ...  
    duration = time.time() - start  
    log_call(api_key, request.client.host, tool_name, req["arguments"], True, str(result), duration)  
except Exception as e:  
    duration = time.time() - start  
    log_call(api_key, request.client.host, tool_name, req["arguments"], False, str(e), duration)  
    raise

四、部署层加固

除了代码层进行授权、验证方面的安全加固,还需要在部署层进行技术层面的安全加固。包括以下措施。

措施说明
强制 HTTPS用 Nginx/Traefik 终止 TLS,禁止 HTTP
IP 白名单仅允许 Agent 服务器 IP 访问 MCP 服务
容器隔离MCP 服务运行在独立 Pod/容器,网络策略限制
密钥管理MCP_API_KEYS 从 Secret Manager 注入,不写死代码

五、其他规范要求

经过以上的技术措施,你的MCP服务框架已经有足够的风险防范能力。

相比于原始的MCP服务,增强后的MCP服务能够抵御以下风险或攻击。

攻击场景原始 fastmcp增强后
未授权调用✅ 成功❌ 401
普通用户调用 admin 工具✅ 成功❌ 403
LLM 注入 {"cmd": "rm -rf /"}✅ 执行❌ 校验失败(若未定义 cmd 字段)
暴力循环调用✅ DoS❌ 429 限流
日志泄露密码✅ 明文记录✅ 自动脱敏

此外,还有一些场景是需要在tool的实现层进行限制。我们可以在以上的基础上增加一些规范要求,进一步规范MCP服务的使用。

1、变更操作必须验证状态并支持一键回退;

2、不允许直接执行智能体输入的SQL;

3、不允许直接操作系统文件;

还有其他情况,可以根据生产运行情况进行补充。