MCP 工具安全设计:为什么 stdio 也要做最小权限与输出过滤

MCP 工具安全设计:为什么 stdio 也要做最小权限与输出过滤

设计 MCP Server 时有个很诱人的捷径:选 stdio,复用应用内部函数,再把函数结果完整返回。没有监听端口,代码也很短,看起来已经足够安全。

我的取舍是把 stdio 只当成传输层收缩,而不把它当成权限边界。工具能力、输入范围、服务端授权、输出字段和重要副作用分别设门禁,所有入口复用同一应用服务。

工程场景与决策冲突

stdio 的优势很明确:本地宿主启动子进程即可通信,默认不暴露远程服务,也不需要为每个工具建立 HTTP 路由。但 Server 进程仍继承自己的文件、网络和凭据权限。

因此真正的冲突不是"stdio 还是 HTTP",而是:为了开发效率,我们希望直接复用内部能力;为了安全和可维护性,我们又不能把内部能力原封不动暴露给模型。

方案拆解与关键权衡

我把边界拆成五个决策:

  1. 工具保持窄动作,不提供万能命令;
  2. 输入使用类型模型,限制路径、数量和长度;
  3. 授权在服务端用例内执行,不相信客户端声明;
  4. 输出通过展示层白名单过滤;
  5. 发布、删除、支付等副作用使用独立一次性批准。

这会比"函数直接注册成工具"多一些样板代码,但换来的收益是入口一致性:CLI、API、MCP 不会各自复制业务规则,也不会因为一个入口漏掉校验而出现能力侧门。

实现链路与最小示例

先定义窄输入:

python 复制代码
class ReadArticleInput(BaseModel):
    article_id: str = Field(min_length=1, max_length=64)
    include_metrics: bool = False

工具层只负责解析和展示:

python 复制代码
def read_article_tool(value: ReadArticleInput, service: ArticleService) -> dict:
    article = service.get_visible_article(
        value.article_id,
        include_metrics=value.include_metrics,
    )
    return {
        "articleId": article.article_id,
        "title": article.title,
        "status": article.status,
    }

不要返回整个领域对象。对象以后新增内部路径、凭据状态或审计字段时,白名单不会跟着自动外泄。

写操作再拆一层:

text 复制代码
prepare -> approve(id, revision, hash, expiry) -> execute once -> verify

批准不是一个长期布尔值,而是绑定精确对象和版本的短期能力。执行超时后先回读事实,不能直接重复调用。

证据、限制与自动化边界

RuyiBookCourse 的 FastAPI MCP 网关章节把"Web 应用功能变成 MCP 工具"定义为能力映射,而不是内部函数裸暴露。真实项目的实现也验证了一个重要结构:stdio MCP、loopback API 与 CLI 都调用同一个服务层;API 默认只绑定回环地址,远程绑定必须显式开启并配置 Token。

这套方案不能把一个本来就高权限的进程变成操作系统级沙箱。如果工具确实需要执行不可信代码,还要加容器、低权限账户、文件系统隔离或专用执行环境。应用层门禁与系统隔离是互补关系。

可复用检查清单

  • 是否存在任意命令、任意 SQL、任意路径工具;
  • 参数是否有类型、长度、数量和目录边界;
  • 服务端是否重新校验身份和对象状态;
  • 输出是否只保留白名单字段;
  • 日志是否可能出现 Token、Cookie、正文或绝对路径;
  • CLI、API、MCP 是否复用同一用例;
  • 重要写操作是否有短期一次性批准;
  • 超时恢复是否先读后写;
  • 测试是否覆盖越权、路径逃逸、敏感输出和重复批准。

收束

stdio 是一个值得采用的默认传输,但不要让"没有端口"替代安全设计。真正可复用的 MCP 边界,是窄工具、服务端授权、输出过滤和可验证副作用共同组成的。

你现在工具列表里权限最宽的是哪一个?如果只能先改一处,我会先把它从万能入口拆成有限动作。

发布前门禁

  • 减少重复背景
  • 突出工程选择而非概念堆砌
  • 不给未经验证的数据结论
  • 本轮无已认领实验,不自行补写实验结论
相关推荐
大可-2 小时前
Go Air 热重载安装配置指南
开发语言·后端·golang
IT_陈寒2 小时前
SpringBoot这个特性差点让我加班到凌晨
前端·人工智能·后端
2401_894915532 小时前
Geo 优化源码部署避坑指南:解决访问异常、定位失效、收录卡顿问题
java·服务器·后端·缓存·开源
Python私教2 小时前
多 Agent 交接如何防串稿:一套内容哈希与回执协议
人工智能·后端
VIP_CQCRE3 小时前
用 Ace Data Cloud Studio,把内容营销变成自动运行的 AI 工作流
ai·自动化·内容营销·mcp·acedatacloud
小则又沐风a3 小时前
负载均衡式在线OJ---------第二幕
linux·c++·后端
不才不才不不才3 小时前
Spring 源码系列(20): @SpringBootApplication 三注解拆解
java·后端·spring
程序员爱钓鱼3 小时前
Rust 生命周期常见错误详解:看懂编译器报错并正确修复
前端·后端·rust
andongni2033 小时前
SpringBoot RESTful API 设计与实现
spring boot·后端·restful