ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

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

MCP实战手记(九):生产化MCP Server的6层安全防护 篇七做了端点鉴权——但那只是一层。本文把散落的安全点串成体系逐层在本机跑通并说清哪些我验证过、哪些没有。引子鉴权过了然后呢篇七给网关加了个 Bearer 校验没带令牌的请求进不来。看起来安全了。但上线第一周就撞了个问题Agent 拿着合法令牌进来能查任何门店的销售数据——天河城店的店长查到了北京路店和深圳店的营业额。原因很直白我们只验证了谁在调用客户端没验证代表谁调用最终用户。前者是鉴权后者是授权中间差着好几层。这就是六层的由来。六层是什么层作用L1 传输安全链路本身可信吗mTLSL2 客户端身份谁在调用CIMD / TokenL3 用户身份透传代表谁调用最终用户L4 访问控制这个用户能看什么RBACL5 流量防护调用多频繁限流 / 熔断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 是代表某个店长在调用服务端得知道这个店长是谁。标准正在成形DPoPAgent Identity WG 在推、ID-JAG已被 EMA 使用、RFC 8693 token exchange、SEP-1933Workload 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-IdL4 访问控制RBAC门店场景天然适合讲这层店长只能查自己门店区域经理可查区域内总部可查全部。我最初把权限判断放在网关层——写完发现行不通网关看到的是get_store_sales(storeCodeST002)这种调用它不知道 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 | methodPOST | resultPASS | userhq | costMs0 AUDIT | path/mcp | methodPOST | resultREJECT_RATE_LIMIT | user- | costMs0被拒绝的请求也要记。只记成功的日志等于出事时看不到攻击。⚠️这层的边界日志现在只落在单实例本地文件——多实例下会分散在各台机器上实例销毁即丢失。生产需要集中采集ELK / 云日志服务并对接防篡改存储否则安全事件里这份日志的证据力很弱。这条我没做。落地优先级2 → 1 → 5 → 4 → 6 → 3顺序层理由1L2 客户端身份最便宜、最标准一步挡掉绝大部分非法调用2L1 mTLS部署侧工作做完链路就可信3L5 限流防自己人误伤比防攻击者更常发生4L4 RBAC需要业务语义但数据一分级就必须做5L6 审计出事能回溯也倒逼前几层做扎实6L3 用户身份标准未定先用过渡方案顶着L3 排在最后原因是它现在没有标准答案——先用断言方案顶着等 DPoP / ID-JAG 落地再替换比现在硬造一套强。如果你的服务只在内部可信网络里跑、且没有数据分级需求L3 和 L4 都可以先不做。小结六层是纵深单层被绕过就全裸别指望一把锁解决所有问题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国内访问 / 点 starhttps://gitee.com/ethanliang2016/mcp-in-actionGitHub需代理https://github.com/ethanliang2016/mcp-in-action克隆git clone https://gitee.com/ethanliang2016/mcp-in-action.git启动java -jar store-ops-security.jar令牌走环境变量未进仓库。跑通了点个 star ⭐跑不通直接提 issue。
返回列表