[控场AI]
· 8 分钟阅读· 4,473 字

从REST API构建安全MCP服务器:OAuth、权限与风险分级实战

从REST API构建安全MCP服务器:OAuth、权限与风险分级实战

从现有REST API构建安全MCP服务器:不暴露危险操作、双套凭证分离、scope与claim锁定调用方身份。

本文以订单API为例,系统梳理了将REST API封装为MCP服务器时的安全实践全流程。核心思路是"分层收口":在工具选择阶段直接不暴露破坏性操作(如DELETE接口),在权限层用OAuth scope限制工具调用资格、用加密转发的claim绑定调用方租户身份,在凭证层将客户端OAuth凭证与后端API密钥严格隔离,在运行层为每次调用打上Read/Write/Destructive风险标签并全量记录日志。文章指出MCP协议本身只解决工具发现与调用的标准化问题,权限边界和危险操作识别必须由开发者在服务器构建阶段主动介入,而非依赖模型的自我约束。

每个团队现在都想做同一件事:让 Claude、Cursor 或内部智能体通过已有的 API 查询订单、核对客户、开工单。而每个安全团队都会反问同一个问题:什么能阻止模型去调用那个执行删除操作的接口?

Model Context Protocol(MCP)只解决了一半问题。它为 AI 客户端提供了发现和调用工具的标准方式。另一半——决定哪些工具存在、谁能调用、什么样的调用算"危险"——仍然要靠你自己把控。

本文梳理如何从一个现有的 REST API 构建 MCP 服务器,并正确处理 MCP 的授权与认证:面向客户端的 OAuth、面向工具的 scope 与 claim,以及给任何会改动数据的操作打上风险分级。文中以 Koodisi 的实践为例(它通过已发布的 API 集合自动生成 MCP 服务器),但这套模式适用于任何你自建的方案。

示例场景:一个订单 API

假设你有一个精简的订单 API,包含三个操作:

操作功能风险
GET /orders/{id}读取订单Read(读)
POST /orders/{id}/refund发起退款Write(写)
DELETE /customers/{id}删除客户Destructive(破坏性)

目标很明确:Claude 可以自由查询订单,可以在带确认步骤的前提下发起退款,而删除接口——它压根就不应该看到。

每个API操作成为一个带风险分级的工具,破坏性操作直接不暴露

第一步:从已发布的 API 和安全配置出发

构建 MCP 服务器前,需要三样东西先就位:

  1. 已发布的 API 集合,并生成了 OpenAPI 规范。规范中的每个操作都是候选工具。
  2. 安全配置(Security Profile),用来定义身份提供方、scope(例如 orders:read、orders:write)以及令牌可携带的 claim(例如 tenant_id)。
  3. 一个运行环境。

安全配置的重要性超出表面。一个服务器只能绑定到一个配置,而你只能授予该配置中已定义的 scope 和 claim。换句话说,没人能把身份提供方从未签发过的权限交给 AI 客户端——这是从源头上收紧权限边界。

OpenAPI 规范(原名 Swagger)是描述 REST API 结构的 JSON/YAML 文件,包含每个端点的路径、HTTP 方法、参数、请求体和响应格式。MCP 服务器生成工具时,本质上是将规范中每个 operationId 对应的操作翻译成一个可供模型调用的函数——函数名、入参类型和描述都来自规范本身。这意味着规范的质量直接决定模型"理解"工具的质量:描述模糊的操作会导致模型猜测该什么时候调用,描述精确的操作则让模型能准确判断意图。因此,在将 API 接入 MCP 之前,值得检查规范中每个操作的 summary 和 description 字段是否清晰表达了该操作的业务语义和副作用,而不仅仅是技术签名。

第二步:创建服务器并挑选工具

在创建流程中,你需要给服务器命名、选择环境与安全配置,然后选取要暴露的已发布操作。本例只暴露了前两个:

  • GET /orders/{id} → 工具,风险分级 Read
  • POST /orders/{id}/refund → 工具,风险分级 Write
  • DELETE /customers/{id} → 不暴露

最后一行是整篇文章里最关键的安全决策。最廉价的护栏,就是一开始就不暴露某个操作。

风险分级分为 Read、Write、Destructive 三类。它们会随每个工具一起传递,这样客户端就能在任何会改动数据的操作(比如退款)前插入确认步骤。

第三步:按工具粒度配置 scope 与 claim

在"APIs & Tools"页面,你可以为整个规范设置默认值(所有工具继承),再为单个工具追加额外要求:

该规范默认值:   scopes: orders:read      claims: tenant_id
refund 工具(追加):   scopes: orders:write

claim 是很多人跳过、随后又后悔的部分。附加在调用方身上的 claim 值会被加密转发到下游 API,这样后端仍然能收到调用方的租户信息并套用自己的常规规则。模型无法靠话术钻进另一个租户的订单——因为租户来自令牌,而非 prompt。

OAuth 2.0 中的 scope 是附加在访问令牌上的权限字符串,用于声明持有者被授权执行哪类操作。当 MCP 服务器在接收到工具调用请求时,会校验请求方携带的令牌中是否包含该工具所要求的 scope——例如 orders:write——不满足则直接拒绝,不会转发到下游 API。Claim 则是令牌载荷(payload)中的键值对字段,常见的有 sub(用户ID)、tenant_id(租户ID)、role 等,由身份提供方(IdP)在颁发令牌时签入,客户端无法自行伪造。将 tenant_id 作为 claim 转发到下游,意味着后端 API 收到的每个请求都携带了经过加密验证的租户信息,无需依赖调用方在请求体或 prompt 中自报家门,从根本上防止了跨租户越权访问。

第四步:为工具配置下游凭证

工具需要向你真实的 API 完成认证。在"Downstream Credentials"中,你为每个安全方案填入 API Key、Bearer Token 或 Basic Auth 值。这些凭证会加密存储,且不再二次显示。

注意这里的职责分离:客户端通过 OAuth 应用向 MCP 服务器认证;工具通过下游凭证向你的后端认证。 AI 客户端永远看不到后端密钥。这是整套架构的安全基石之一。

这里的"职责分离"对应信息安全中的 Separation of Concerns 原则,在凭证管理上具体体现为两个独立的信任链:其一,AI 客户端(Claude、Cursor 等)持有 OAuth client_id 和 client_secret,只能向 MCP 服务器证明"我是谁、我有哪些权限",无法接触到后端 API 的真实密钥;其二,MCP 服务器在确认客户端身份与 scope 合法后,才使用自己保管的下游凭证向后端发起请求。这样即使 AI 客户端被恶意 prompt 注入或会话被劫持,攻击者也只能以该客户端已有的 scope 范围行事,而无法提取或复用后端凭证去直接绕过 MCP 层调用 API。这一架构也方便凭证轮换:更换后端 API Key 只需更新服务器端配置,所有接入的客户端无需感知。

第五步:用 OAuth 应用做客户端认证

在"Client Access"中新建 OAuth 应用,设置 scope 和默认 claim,分配用户,拿到 client_id 和 client_secret(只显示一次,务必复制保存)。

对组织外部的人员,可以开启"接受外部客户端"并分享注册链接。注册请求会先进入 Pending(待审) 状态,在有人批准前,登录会返回 401。这正是一个值得每个 API 都拥有的审核队列。

第六步:接入 Claude 之前先测试

"Connect"页签可以生成一个短期有效的测试令牌,让你用纯 JSON-RPC 直接访问服务器。MCP 基于 JSON-RPC 2.0,列出工具的请求是这样的:

curl -s "$MCP_ENDPOINT" \
  -H "Authorization: Bearer $TEST_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

你应当恰好看到两个工具。如果看到三个,回第二步检查。

第七步:把 Claude(或 Cursor)接到 MCP 服务器

同一个"Connect"页签能为 Claude、Cursor、Gemini CLI、mcp-remote、Python 或 cURL 生成配置片段。以 Claude Desktop 为例:进入 Settings → Developer → Edit Config,把片段粘贴进 claude_desktop_config.json,填好 client ID 和 secret,重启即可。Claude 会通过 OAuth 完成登录,工具随即出现。

然后试一下最直观的指令:

"订单 1042 的状态是什么?如果还没发货,就退款。"

Claude 会用读取工具查到订单,而标记为 Write 的退款工具,正是客户端可以放在确认步骤之后的那个。无论如何,删除接口都不在菜单上。

读取操作直接执行,Write级别的退款则由客户端请求确认

第八步:盯住它到底做了什么

"Analytics"页签展示总调用数、成功率、错误、被阻止的调用以及 p95 延迟,并可按工具或用户维度拆分;"Activity log"则让你定位到每一次具体调用。当有人问"那笔退款是 AI 发起的吗?",你可以拿记录回答,而不是靠猜。

几点核心经验

  • 少暴露。 最安全的破坏性工具,就是根本不存在的那个。
  • 标注风险。 Read、Write、Destructive 给了客户端一个停下来询问的标准理由。
  • 绑定身份,而非 prompt。 scope 与转发的 claim 把模型锁在调用方的权限范围内。
  • 分离两套凭证。 客户端用 OAuth,后端密钥留在服务器端。
  • 记录每一次调用。 智能体以机器速度行动,逐次调用的日志不是可选项。

智能体接入企业 API 的浪潮正在到来,而把它们关进可控的笼子里,比让它们"能跑起来"重要得多。这套从 OpenAPI 规范出发、以风险分级和双重凭证收口的模式,为团队提供了一个可落地的起点。

#MCP服务器#REST API#OAuth认证#AI智能体#风险分级#MCP授权#API治理#Model Context Protocol
分享:

相关推荐