原创 Paper | MCP 安全探讨系列(一)—— MCP 概述

admin 2025年4月21日21:57:56评论13 views字数 8831阅读29分26秒阅读模式
者:Hcamael@知道创宇404实验室
时间:2025年4月18日

1. 前言

参考资料

近些年,人类在生成式人工智能的道路上一路狂奔,ChatGPT的横空出世、多模态模型的发展、Agent浪潮开启、DeepSeek的发布,让我们经历着AI发展史上一个又一个的群星闪耀时。当各家模型可以有九种方法查询天气时,模型上下文协议(MCP)却在以一种统一的姿态让模型自主构建理解本身的含义。给予模型一种通用的协议格式,让模型自主选择与世界交互的方法,模型上下文协议(MCP)用他的理解建造着新的巴别塔。

MCP(Model Context Protocol,模型上下文协议),2024年11月底,由 Claude(Anthropic) 推出的一种开放标准,旨在统一大型语言模型(LLM)与外部数据源和工具之间的通信协议。MCP 的主要目的在于解决当前 AI 模型因数据孤岛限制而无法充分发挥潜力的难题,MCP 使得 AI 应用能够安全地访问和操作本地及远程数据,为 AI 应用提供了连接万物的接口。

Claude 发布了 MCP 后,官方的 Claude Desktop 就开放了 MCP 功能,并且推动了开源组织 Model Context Protocol[1],由不同的公司和社区进行参与。丰富的社区和支持是协议被越来越多的人接受,形成统一的生态的关键。架构上的安全可靠与可拓展性是协议后续持续发展的积极动力。本文将会从MCP实现原理说起,介绍与MCP相关的安全设计,已知的安全问题以及可能存在安全风险的位置。

2. MCP发展、实现与架构

参考资料

当大语言模型从与人类对话出发,向着会动手做事发展时,就踏上了一条不断自我革新与突破的路程。就像人类进化过程中学会了使用火,大语言模型与现实开始交互就是从学会使用工具开始。这里的工具是程序员们写的一个个函数(Function),大语言学会调用(Call)正确的函数获取正确的结果并不断思考,就是Function Call的过程。

在大语言模型发展的初期,业界尝试过让大语言模型自主规划任务内容、调用工具、获取执行结果,这诞生了 BabyAGI 和 AutoGPT 等工具。 BabyAGI 通过将用户输入的目标拆分成一个个小目标后,依次执行每个小目标,直到结束。但是这种方案依赖于每个小目标执行的函数,一旦某个小目标没有对应的函数,最终效果都将受到限制。AutoGPT考虑地就更加全面,不仅添加了长期记忆模块,还有根据执行结果自我反馈和重试的机制来不断完善。

大语言模型蓬勃发展推动了一系列框架工具的兴起。在 LangChain 框架中的 Tool,其本质就是基于 Function Call 思路构建的。比如在 LangChain 中定义了多种工具(Tool),LangChain中的智能体(Agent)会通过大语言模型判断当前是否需要使用工具和工具的参数。LangChain框架在调用对应工具获取结果后,再次通过大语言模型生成对应的回答给用户。在整个过程中,大语言模型会选择何时去调用工具,但大模型无法改变 LangChain 框架下智能体的预定行为。这在某些特殊情况下便不够灵活。

与此同时,学术界也在尝试将函数调用的功能添加到大模型本身。AI可以根据输入的内容在合适的地方调用对应函数的功能,这也造就了 ChatGPT 插件功能诞生。程序员可以为ChatGPT编写插件,AI可以调用插件计算结果、查询天气、浏览网页,开始具备“行动”能力。

现实世界中的工作复杂且繁琐,不同的程序员可以为同一个功能写出不同的代码。复杂中需要统一,这成为了 MCP 流行的契机。

3. MCP的实现原理

参考资料

3.1 MCP 的实现过程

在MCP官网的介绍是这么写的:

MCP 是一个开放协议,它规范了应用程序向 LLM 提供上下文的方式。MCP 就像 AI 应用程序的 USB-C 端口一样。正如 USB-C 提供了一种标准化的方式将您的设备连接到各种外围设备和配件一样,MCP 也提供了一种标准化的方式将 AI 模型连接到不同的数据源和工具。

你可能难以理解这段话,但 claudemcp 网站上有一张图很好地说明了 MCP 的架构特点:

原创 Paper | MCP 安全探讨系列(一)—— MCP 概述

MCP就是你计算机上的拓展坞,用于连接各种不同的外设,通过这个扩展坞,不管你的设备(MCP Server)使用的是USB A/B/C,或者HDMI,你都能通过MCP这个拓展坞来连接到电脑(MCP Client)。电脑可以通过拓展坞直接获取设备本地数据资源、远程服务器上的邮件等。

3.2 通信机制

MCP 协议支持两种主要的通信机制:基于标准输入输出的本地通信和基于SSE(Server-Sent Events)的远程通信。这两种机制都使用 JSON-RPC 2.0 格式进行消息传输,确保了通信的标准化和可扩展性。

本地通信:通过 stdio 传输数据,适用于在同一台机器上运行的客户端和服务器之间的通信。 远程通信:利用 SSE 与 HTTP 结合,实现跨网络的实时数据传输,适用于需要访问远程资源或分布式部署的场景。

MCP的协议规范可以参考:https://github.com/modelcontextprotocol/modelcontextprotocol/tree/main/schema

接下来,我们通过python的fastmcp库来快速认识一下MCP协议。

一个简单的MCP Server如下所示:

$ cat mcpServer.py#!/usr/bin/env python3# -*- coding=utf-8 -*-from fastmcp import FastMCP# Create an MCP servermcp = FastMCP("Demo", debug=True, log_level="DEBUG")# Add an addition tool@mcp.tool()defadd(a: int,b: int)-> int:"""Add two numbers"""returna+b@mcp.tool()def echo_tool(message: str)-> str:"""Echo a message as a tool"""returnf"Tool echo: {message}"if __name__ =="__main__":    mcp.run(transport="sse")

这里的通信协议选择SSE,方便抓包分析。接着,我们需要两个窗口来运行命令:

# 第一个窗口,启动mcp Server$ python3 mcpServer.py[04/16/25 11:17:59] DEBUG    Using selector: KqueueSelector                                                                    selector_events.py:64                    DEBUG    SseServerTransport initialized with endpoint: /messages                                                       sse.py:79INFO:     Started server process [29940]INFO:     Waiting for application startup.INFO:     Application startup complete.INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)# 从上面可以看到SSE协议监听本地的8000端口# =====================================# 接着第二个窗口,启动调试客户端$ fastmcp dev mcpServer.pyStarting MCP inspector...⚙️ Proxy server listening on port 6277? MCP Inspector is up and running at http://127.0.0.1:6274 ?

现在,我们在浏览器中打开:http://127.0.0.1:6274,如下图所示:

原创 Paper | MCP 安全探讨系列(一)—— MCP 概述

协议选择SSE,URL填入对应地址,然后点击连接。

连接成功后,由于我们的MCP Server只定义了两个工具,所以我们直接点击Tools,接着点击List Tools,获取所有工具,如下图所示:

原创 Paper | MCP 安全探讨系列(一)—— MCP 概述
原创 Paper | MCP 安全探讨系列(一)—— MCP 概述

在上图中,可以看到我们成功获取到了我们的Server中定义的两个工具:add,echo_tool。并且在下面的History中,可以看到完整的协议信息:

客户端发送的请求为:

{  "method": "tools/list",  "params": {}}// 通过抓包或者DEBUG信息,可以得知实际的JSON-RPC 2.0数据包{    "jsonrpc":"2.0",    "id":1,    "method":"tools/list",    "params":{}}

该请求可以和官方的协议规范对应上,如下所示:

"ListToolsRequest": {    "description": "Sent from the client to request a list of tools the server has.",    "properties": {        "method": {            "const": "tools/list",            "type": "string"        },        "params": {            "properties": {                "cursor": {                    "description": "An opaque token representing the current pagination position.nIf provided, the server should return results starting after this cursor.",                    "type": "string"                }            },            "type": "object"        }    },    "required": [        "method"    ],    "type": "object"}

或者我们还可以和python的mcp库中的代码对应上,如下所示:

class JSONRPCRequest(Request[dict[str, Any]| None, str]):"""A request that expects a response."""    jsonrpc: Literal["2.0"]    id: RequestId    method: str    params: dict[str, Any]| None = Noneclass ListToolsRequest(PaginatedRequest[RequestParams | None, Literal["tools/list"]]):"""Sent from the client to request a list of tools the server has."""    method: Literal["tools/list"]    params: RequestParams | None = None

我们接收到的响应结果如下所示:

{  "tools": [    {      "name": "add",      "description": "Add two numbers",      "inputSchema": {        "type": "object",        "properties": {          "a": {            "title": "A",            "type": "integer"          },          "b": {            "title": "B",            "type": "integer"          }        },        "required": [          "a",          "b"        ],        "title": "addArguments"      }    },    {      "name": "echo_tool",      "description": "Echo a message as a tool",      "inputSchema": {        "type": "object",        "properties": {          "message": {            "title": "Message",            "type": "string"          }        },        "required": [          "message"        ],        "title": "echo_toolArguments"      }    }  ]}// 通过抓包或者DEBUG信息,可以得知实际的JSON-RPC 2.0数据包{    "jsonrpc":"2.0",    "id":1,    "result":{        "tools":[            {                "name":"add",                "description":"Add two numbers",                "inputSchema":{                    "properties":{                        "a":{                            "title":"A",                            "type":"integer"                        },                        "b":{                            "title":"B",                            "type":"integer"                        }                    },                    "required":["a","b"],                    "title":"addArguments",                    "type":"object"                }            },            {                "name":"echo_tool",                "description":"Echo a message as a tool",                "inputSchema":{                    "properties":{                        "message":{                            "title":"Message",                            "type":"string"                        }                    },                    "required":["message"],                    "title":"echo_toolArguments",                    "type":"object"                }            }        ]    }}

响应的格式同样在协议规范中和源码中有响应的定义,如下所示:

"ListToolsResult": {    "description": "The server's response to a tools/list request from the client.",    "properties": {        "_meta": {            "additionalProperties": {},            "description": "This result property is reserved by the protocol to allow clients and servers to attach additional metadata to their responses.",            "type": "object"        },        "nextCursor": {            "description": "An opaque token representing the pagination position after the last returned result.nIf present, there may be more results available.",            "type": "string"        },        "tools": {            "items": {                "$ref": "#/definitions/Tool"            },            "type": "array"        }    },    "required": [        "tools"    ],    "type": "object"}......"Tool": {    "description": "Definition for a tool the client can call.",    "properties": {        "annotations": {            "$ref": "#/definitions/ToolAnnotations",            "description": "Optional additional tool information."        },        "description": {            "description": "A human-readable description of the tool.nnThis can be used by clients to improve the LLM's understanding of available tools. It can be thought of like a "hint" to the model.",            "type": "string"        },        "inputSchema": {            "description": "A JSON Schema object defining the expected parameters for the tool.",            "properties": {                "properties": {                    "additionalProperties": {                        "additionalProperties": true,                        "properties": {},                        "type": "object"                    },                    "type": "object"                },                "required": {                    "items": {                        "type": "string"                    },                    "type": "array"                },                "type": {                    "const": "object",                    "type": "string"                }            },            "required": [                "type"            ],            "type": "object"        },        "name": {            "description": "The name of the tool.",            "type": "string"        }    },    "required": [        "inputSchema",        "name"    ],    "type": "object"}
class Tool(BaseModel):"""Definition for a tool the client can call."""    name: str"""The name of the tool."""    description: str | None = None"""A human-readable description of the tool."""    inputSchema: dict[str, Any]"""A JSON Schema object defining the expected parameters for the tool."""    model_config = ConfigDict(extra="allow")class ListToolsResult(PaginatedResult):"""The server's response to a tools/list request from the client."""    tools: list[Tool]

通过上述例子,我们可以看出,一个MCP Server的工具,至少需要三个属性:工具名称,工具描述,工具需要的参数信息。

当我们需要调用一个工具时,构造的数据如下所示:

{  "method": "tools/call",  "params": {    "name": "add",    "arguments": {      "a": 233,      "b": 455    }  }}

得到的结果如下所示:

{  "content": [    {      "type": "text",      "text": "688"    }  ],  "isError": false}

以上部分可以说就是MCP协议的内容,MCP协议制定的标准。接下来我们再看看各种MCP应用,比如官方的Cursor,还有Windsurf,或者VSCode上的Cline,除了和MCP Server的通信协议一致,其他部分都各有区别。可以说是不同厂商开发了各自的MCP Client。虽然这些应用出自不同的厂商,但是他们都可以互相使用对方商店中的MCP Server,这就是MCP协议的优势。

但是从安全上看,单看协议很难发现安全问题,所以我们需要结合实际应用的代码实现,来分析不同应用的MCP都有哪些安全问题。

4. 参考链接

参考资料
Model Context Protocol:https://github.com/modelcontextprotocol
原创 Paper | MCP 安全探讨系列(一)—— MCP 概述

作者名片

原创 Paper | MCP 安全探讨系列(一)—— MCP 概述
原创 Paper | MCP 安全探讨系列(一)—— MCP 概述
往 期 热 门
(点击图片跳转)
原创 Paper | MCP 安全探讨系列(一)—— MCP 概述
原创 Paper | MCP 安全探讨系列(一)—— MCP 概述
原创 Paper | MCP 安全探讨系列(一)—— MCP 概述
原创 Paper | MCP 安全探讨系列(一)—— MCP 概述
“阅读原文”更多精彩内容!

原文始发于微信公众号(知道创宇404实验室):原创 Paper | MCP 安全探讨系列(一)—— MCP 概述

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年4月21日21:57:56
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   原创 Paper | MCP 安全探讨系列(一)—— MCP 概述http://cn-sec.com/archives/3981526.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息