首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >MCP 权限模型实战:工具粒度授权、确认 UI 设计与多 server 隔离

MCP 权限模型实战:工具粒度授权、确认 UI 设计与多 server 隔离

原创
作者头像
用户11136834
发布于 2026-10-08 22:16:29
发布于 2026-10-08 22:16:29
90
举报

一、模型的"手"该被允许做什么

系列前两篇解决了"server 从哪来"(供应链)和"工具描述能不能信"(投毒防御),这篇聊最后一块拼图:权限模型。前两篇的所有防御都有一个隐含前提——你已经决定了哪些工具可以被调用、哪些参数可以被接受。这个"决定"本身就是一套需要设计的系统。

一个真实场景:你给 Agent 接了 GitHub server,工具列表里有 list_issues 也有 delete_file。用户随口一句"帮我清理一下仓库里没用的东西",模型完全可能推导出"清理 = 删文件"。权限模型的作用,就是让这类推导在系统层面走不通,而不依赖模型每次都"懂事"。

二、工具粒度授权:三层 allowlist

不要在 server 级别做开关(要么全开要么全关),要在工具级别分层:

代码语言:json
复制
{
  "mcpServers": {
    "github": {
      "toolPolicy": {
        "default": "deny",
        "allow": [
          { "tool": "list_*", "level": "read" },
          { "tool": "get_*", "level": "read" },
          { "tool": "create_issue", "level": "write" },
          { "tool": "delete_*", "level": "dangerous", "requireConfirmation": true }
        ]
      }
    }
  }
}

三条设计原则:

  1. 默认拒绝:新接入的 server 先全 deny,按需开放,而不是全开后再收
  2. 通配符按前缀收敛:list*、get* 这类只读前缀可以放开,写操作逐个列出
  3. 级别决定流程:read 直接放行,write 弹一次确认,dangerous 弹确认 + 参数二次校验 + 审计落盘

三、确认 UI 的信息设计:让人真的能拦住

弹确认框不等于有人看。信息设计决定确认的有效性:

  • 展示填充后的完整参数,不是工具名。"调用 send_email" 没有信息量;"收件人 = attacker@evil.com" 才能拦住攻击
  • 高风险参数高亮:收件人、金额、删除目标路径、外发数据量,用视觉权重区分
  • 展示推导链:把"用户原话 → 模型决定"的对应关系折叠展示,让人能一眼看出模型是不是理解错了
  • 危险操作冷却:delete 类操作强制 3 秒倒计时,防手滑也防"连环确认"式的自动化滥用

反例是那种"确定要继续吗?"的无信息确认框,用户会形成"无脑点确定"的肌肉记忆——确认疲劳比没有确认更危险,因为它污染了所有确认的可信度。

四、多 server 隔离:防"借刀杀人"

当客户端同时挂了多个 server,新的风险出现了——权限混淆(confused deputy):

场景:文件系统 server 有读 ~/.ssh 的权限,网络 server 有出网权限。单独看都合理,但模型可以把从前者读到的内容喂给后者发出去。两个"低危" server 组合出了一个"高危"能力。

防御要点:

代码语言:text
复制
1. token 不共享:每个 server 用独立凭证,scope 最小化
2. 数据流标注:客户端对 server 产出内容打标,跨 server 传递敏感标记内容时强制确认
3. 会话隔离:一次任务会话内只激活相关 server,不搞"全家桶常驻"
4. 出网白名单按 server 划分:文件 server 没有任何出网理由

其中数据流标注成本最高,但它是唯一能拦住"组合攻击"的机制——单 server 视角的权限检查在多 server 场景下天然有盲区。

五、一个最小可用的策略引擎

不需要重型框架,客户端启动时加载策略文件,每次工具调用前过一遍:

代码语言:python
复制
import fnmatch, json

class Policy:
    def __init__(self, path):
        self.rules = json.load(open(path))["toolPolicy"]

    def check(self, server: str, tool: str, args: dict):
        rule = self.rules.get(server, {})
        for item in rule.get("allow", []):
            if fnmatch.fnmatch(tool, item["tool"]):
                level = item["level"]
                if level == "read":
                    return True, None
                confirm = {"confirmed": item.get("requireConfirmation", False),
                           "args": args, "level": level}
                return level != "dangerous", confirm
        return False, None  # default deny

三十行代码,换来的是所有工具调用都有明确的策略裁决点——审计和回放也顺手解决了。

六、检查清单

  1. 工具级 allowlist,默认 deny,新 server 先只读观察
  2. read/write/dangerous 三级,级别对应不同确认流程
  3. 确认框展示实际参数,高风险参数高亮,危险操作加冷却
  4. 多 server 场景:token 独立、会话隔离、敏感数据流标注
  5. 出网白名单按 server 划分,文件类 server 默认断网
  6. 所有授权决策落审计日志,能回答"当时谁允许的"

七、系列收官

三篇连起来,MCP 安全的完整链路:server 从哪来(供应链)→ 描述能不能信(投毒防御)→ 调用被允许做什么(本篇)→ 出事怎么查(审计)。安全从来不是某个单点配置,而是这条链上每一环都不省事。

后面如果时间允许,会再写一篇实战向的:把三篇的防御整合成一个开箱即用的 MCP 客户端配置模板。有问题欢迎评论区交流。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、模型的"手"该被允许做什么
  • 二、工具粒度授权:三层 allowlist
  • 三、确认 UI 的信息设计:让人真的能拦住
  • 四、多 server 隔离:防"借刀杀人"
  • 五、一个最小可用的策略引擎
  • 六、检查清单
  • 七、系列收官
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档