MCP实战手记(九):生产化MCP Server的6层安全防护

篇(七)做了端点鉴权------但那只是一层。

本文把散落的安全点串成体系,逐层在本机跑通,并说清哪些我验证过、哪些没有。

引子:鉴权过了,然后呢

篇(七)给网关加了个 Bearer 校验,没带令牌的请求进不来。看起来安全了。

但上线第一周就撞了个问题:Agent 拿着合法令牌进来,能查任何门店的销售数据------天河城店的店长,查到了北京路店和深圳店的营业额。

原因很直白:我们只验证了"谁在调用"(客户端),没验证"代表谁调用"(最终用户)。前者是鉴权,后者是授权,中间差着好几层。

这就是六层的由来。


六层是什么

层 作用
L1 传输安全 链路本身可信吗(mTLS)
L2 客户端身份 谁在调用(CIMD / Token)
L3 用户身份透传 代表谁调用(最终用户)
L4 访问控制 这个用户能看什么(RBAC)
L5 流量防护 调用多频繁(限流 / 熔断)
L6 审计追踪 发生过什么(结构化日志)

单层的失效方式很一致:任何一层被绕过,后面就全裸。所以这六层不是选做清单,是纵深。

范围说明:这六层覆盖的是调用链安全 ------"谁在调、能调什么、调多快、留没留痕"。内容安全(输入校验、输出过滤、Prompt 注入防护)是另一条线,本篇不涉及,别以为 MCP 安全只有这六件事。


L1 传输安全(mTLS)

双方互验证书,服务端确认客户端身份,客户端也确认服务端不是冒充的。

⚠️ 本机未实测 :mTLS 需要签发客户端证书、配置 truststore,属于部署侧工作。本模块只给了 SSL 配置位与"在哪一层终止"的说明,真实双向认证我还没在真实证书体系下跑过。

一个决策点:mTLS 在哪一层终止?

  • 在网关/接入层终止:后端拿到的是明文,简单,但要求内网可信
  • 透传到应用:更安全,证书管理成本高

判断依据就一条:你能不能接受内网明文。有服务网格兜底就选接入层终止,没有就透传到应用。我倾向前者。


L2 客户端身份

篇(四)讲过 CIMD(用 HTTPS URL 当 client_id)。本篇用静态 Bearer Token 替代演示------CIMD 要求服务端能 GET 到客户端的 metadata URL,本机演示环境把这个环节省掉了。

这不代表 CIMD 只能公网用,内网结合自签 CA 同样可行,只是这条路我没验证。

实测(无令牌 / 错令牌):

复制代码
HTTP 401  {"error":"invalid client token"}

两层判断:有没有令牌、令牌对不对。这两步是所有防护的地基,做起来也最便宜。


L3 用户身份透传(最难的一层)

客户端身份 ≠ 用户身份。Agent 是代表某个店长在调用,服务端得知道这个店长是谁。

标准正在成形:DPoP(Agent Identity WG 在推)、ID-JAG(已被 EMA 使用)、RFC 8693 token exchange、SEP-1933(Workload Identity Federation)。标准落地前没有公认做法。

我的过渡方案:请求头带 X-User-Id,服务端提取后放进 UserContext,工具层取用。

⚠️ 这层的局限,讲透 :

这是身份断言,不是身份验证 ------X-User-Id 是客户端自己声明的,服务端选择信任,没有任何密码学校验。

它防不住:伪造用户、委托链、凭证轮换、audience/issuer 校验。

适用范围仅限内部可信网络。真实生产要等标准落地(DPoP / ID-JAG),或自建签发校验。

我宁可把它当"局限"讲,也不包装成"生产级安全能力"------资深读者一眼能看出差别。
生产后长什么样(迁移路径) :现在用 X-User-Id 顶,是因为标准没定。标准落地后,这一层会换成可验证的身份令牌 ------客户端带一个由可信签发方签发的、含 issuer + audience 校验的令牌(DPoP / ID-JAG 都是往这个方向走),服务端验证签名、而不是信任声明。到那时"断言"才升级成"验证",本文 L3 的过渡方案自然退役。这是把当前做法当"过渡"而不是"终点"的原因。

实测(缺用户身份):

复制代码
"拒绝:未携带用户身份(缺 X-User-Id)"

L4 访问控制(RBAC)

门店场景天然适合讲这层:店长只能查自己门店,区域经理可查区域内,总部可查全部。

我最初把权限判断放在网关层------写完发现行不通:网关看到的是 get_store_sales(storeCode=ST002) 这种调用,它不知道 ST002 属于谁的数据。判断能不能看,只有工具自己知道参数语义。于是挪到了工具层。

实测六种组合:

用户(角色) 查 结果
manager-st001(店长) ST001(自己) ✅ 天河城店 12,480 元
manager-st001(店长) ST002(别人) ❌ 无权访问
manager-st002(店长) ST002(自己) ✅ 北京路店 9,860 元
regional-gz(区域) ST001(区域内) ✅ 放行
regional-gz(区域) ST003(区域外) ❌ 无权访问
hq(总部) ST003 ✅ 深圳华强北店 15,230 元
stranger(无角色) ST001 ❌ DENIED

判断逻辑就一段 switch,位置比逻辑本身更重要。

进阶取舍(写给会做到几十个工具的人) :工具少时"每工具一段 switch"够用;工具一多就散------权限判断散落在各工具里,改一条策略要改多处、还容易漏。生产上更常见的做法是把策略集中:一个中央授权服务(或 policy 引擎)持有"用户 → 角色 → 工具/资源 → 动作"的规则,各工具通过统一拦截器调它判定,自己不再写判断。"ST002 属于谁"这类资源归属下沉成一张归属表,由授权服务查。这条路我在第(十三)篇多租户细讲,本篇只点这个方向------知道"位置比逻辑重要",也要知道"位置最终要收敛到一处"。


L5 流量防护(限流)

用的 Bucket4j,规格 5 次 / 10 秒。实测连发 8 次:

复制代码
200 200 200 200 200 429 429 429

前 5 次放行,第 6 次起被拦。令牌桶按 5/10s 贪婪补充。

⚠️ 一个我踩到的坑 :这里的桶是内存 Map 。单实例没问题,多实例部署时每个实例各算各的,放行上限变成实例数 × 5(实际能放多少还看请求怎么分发)。真实生产必须把计数外置(Redis,或在网关层统一限流)。

这条我在单机上验证不出来,是看代码推出来的------所以标为待生产验证,不假装已验证。


L6 审计追踪

每次调用打一条结构化日志,含路径、方法、判定结果、用户、耗时:

复制代码
AUDIT | path=/mcp | method=POST | result=PASS | user=hq | costMs=0
AUDIT | path=/mcp | method=POST | result=REJECT_RATE_LIMIT | user=- | costMs=0

被拒绝的请求也要记。只记成功的日志,等于出事时看不到攻击。

⚠️ 这层的边界 :日志现在只落在单实例本地文件------多实例下会分散在各台机器上,实例销毁即丢失。生产需要集中采集(ELK / 云日志服务),并对接防篡改存储,否则安全事件里这份日志的证据力很弱。这条我没做。


落地优先级:2 → 1 → 5 → 4 → 6 → 3

顺序 层 理由
1 L2 客户端身份 最便宜、最标准,一步挡掉绝大部分非法调用
2 L1 mTLS 部署侧工作,做完链路就可信
3 L5 限流 防自己人误伤,比防攻击者更常发生
4 L4 RBAC 需要业务语义,但数据一分级就必须做
5 L6 审计 出事能回溯,也倒逼前几层做扎实
6 L3 用户身份 标准未定,先用过渡方案顶着

L3 排在最后,原因是它现在没有标准答案------先用断言方案顶着,等 DPoP / ID-JAG 落地再替换,比现在硬造一套强。如果你的服务只在内部可信网络里跑、且没有数据分级需求,L3 和 L4 都可以先不做。


小结

  1. 六层是纵深:单层被绕过就全裸,别指望一把锁解决所有问题
  2. RBAC 的位置比逻辑重要:网关不懂参数语义,判断得落在工具层

本机已验证 / 待生产验证

项 状态
L2 令牌校验(401) ✅ 本机已验证
L3 身份提取与缺失拒绝 ✅ 本机已验证(断言方案,非验证)
L4 RBAC 六种组合 ✅ 本机已验证
L5 限流触发(429) ✅ 单实例已验证;多实例计数待生产验证
L6 审计日志输出 ✅ 本机已验证;集中采集 / 防篡改待生产验证(现为单实例本地文件)
L1 mTLS 双向证书 ❌ 未验证,仅给配置位与部署建议
CIMD 公网 metadata ❌ 未验证(本机用静态 Token 替代)
真实 IAM 下的身份校验 ❌ 未验证

环境:Spring Boot 4.0.0 / Spring AI 2.0.1 / MCP Java SDK 2.0.0 / JDK 21 / Bucket4j 8.14。

示例代码仅用于技术演示,令牌为 demo 值,生产请走 CIMD/EMA 体系。


📦 本文完整可运行代码 (tag: v09,模块 09-store-ops-security,端口 8090)

启动:java -jar store-ops-security.jar(令牌走环境变量,未进仓库)。

跑通了点个 star ⭐,跑不通直接提 issue。

相关推荐
红海云1 小时前
Jev放开之后,Agent开始分工
人工智能
阳光九叶草LXGZXJ1 小时前
Linux-学习-12-JDK安装
java·linux·运维·学习
灰山君2 小时前
面向高校实训室的数字孪生可视化平台:支持校内部署与200个教学席位
大数据·人工智能·信息可视化·数据可视化·实时大数据
马剑威(威哥爱编程)2 小时前
【AI全栈后端12-04】Spring Boot 用结构化输出自动解析简历:让模型按你的 POJO 输出
java·人工智能·spring boot·后端
海盗12342 小时前
AI 新闻日报 2026-09-30:智能体边界下沉到芯片、微软统一多智能体 SDK、具身工具链走向智能体可用
人工智能·microsoft·机器人·人工智能aigc
代数狂人3 小时前
机器学习数学基础──第 4 章 矩阵:数据的仓库与批量计算的引擎
人工智能·机器学习·矩阵
Hi202402177 小时前
Vortex CUDA 生态适配:让 CUDA C、CUTLASS 与 Triton 在 RISC-V GPGPU 上运行
人工智能·risc-v·gpgpu
xcl09258 小时前
北京24小时自助健身房系统软件开发实战:从架构到部署全流程指南
java·spring boot·架构
龙腾AI白云10 小时前
AI检索增强生成(RAG):解决大模型幻觉的核心落地技术
数据库·人工智能·机器学习·知识图谱