MCP Server插件权限漏洞分析:修复未认证访问导致的数据库泄露风险

MCP Server插件权限漏洞剖析:MCP协议安全边界实战笔记
一、真实问题现场
某MCP Server插件上线后不久,客户反馈数据库被全量导出。日志显示,一个未认证的HTTP请求调用了/tools/list_tools端点,返回结果里赫然包含users、payments、auth_sessions等表名,以及字段定义和索引信息。攻击者据此构造SQL注入载荷,绕过应用层直接读取敏感数据。
这不是理论风险——是已发生的生产事故。
二、漏洞根因拆解
权限校验完全缺失
插件收到MCP请求时,直接执行工具逻辑,未检查request.context.permissions字段。MCP协议要求Server在调用任何工具前验证权限上下文,但该插件把permissions当成了可选字段处理。
后果很直接:任意客户端(包括curl、Postman)都能触发list_tools,而这个工具又没做输入过滤或输出脱敏。
mcp.tools.list_tools暴露结构信息
该工具本意是供Agent运行时动态发现可用能力,但插件实现中:
- 直接返回了数据库连接池的元数据(
pg_catalog查询结果) - 未剥离
information_schema中的敏感字段(如column_default含密码哈希逻辑) - 返回体未做角色过滤——管理员和访客看到的工具列表完全一致
元数据不是“辅助信息”,它是攻击链的第一块垫脚石。
RBAC形同虚设
插件声明支持RBAC,但实际只做了两件事:
- 在配置文件里写了
"role": "admin"硬编码 - 所有工具函数都忽略
context.role参数
结果:普通用户调用delete_user时,代码路径和管理员完全相同。
三、Supabase场景下的放大效应
当MCP Server对接Supabase时,问题更致命。Supabase默认开启Row Level Security(RLS),但前提是客户端必须提供有效JWT。而该插件:
- 用服务端私钥直连Supabase,绕过RLS
list_tools返回的表名直接映射到Supabase的公开视图名- 攻击者拿到
auth.users表结构后,立刻用https://your-project.supabase.co/rest/v1/auth.users?select=*发起跨域请求
Supabase没破防,是MCP Server把门钥匙交给了路人。
四、守住MCP安全边界的实操要点
协议规范的关键约束
MCP协议对权限的强制要求就三条,必须落地:
- 所有工具调用前,必须校验
context.permissions是否包含当前操作所需scope list_tools返回的工具列表,必须按context.role动态过滤(例如user角色看不到db.dump)context对象不可被客户端篡改——Server需用签名验证其完整性
Server开发必须做的三件事
权限校验前置
在路由层统一拦截,拒绝permissions为空或缺失必要scope的请求:def require_permission(scope: str): if not request.context.permissions or scope not in request.context.permissions: raise PermissionError(f"Missing permission: {scope}")工具元数据严格分级
list_tools返回值按角色隔离:guest: 仅返回weather.get、calculator.adduser: 增加db.query(但隐藏table参数枚举)admin: 才返回db.dump、system.exec
禁用危险反射
禁止工具函数通过getattr()动态调用数据库方法。所有DB操作必须显式声明依赖的表和字段,并在启动时注册白名单:# 正确:白名单驱动 DB_OPERATIONS = { "users.read": {"tables": ["users"], "fields": ["id", "email"]}, "payments.write": {"tables": ["payments"], "fields": ["amount", "status"]} }
商业化案例中的硬性红线
医疗AI Agent的MCP Server部署时,我们强制执行:
- 所有数据库操作必须走预编译语句(
PreparedStatement),禁止拼接SQL context.permissions由OIDC ID Token解码生成,不接受客户端传入的任何权限字段- 每次
list_tools调用触发审计日志,记录client_ip、user_role、timestamp - 敏感工具(如
db.export)需二次确认,且导出文件自动加密+72小时过期
这些不是“最佳实践”,是HIPAA合规的底线。
五、yitb.com的定位
yitb.com不做MCP协议翻译器,只做三件事:
- 把协议规范里的安全条款,转换成可测试的代码检查清单(比如“
list_tools响应体必须包含role字段”) - 公开真实漏洞的复现环境和修复diff(含Supabase、Postgres、MongoDB三套方案)
- 拆解商业化Agent的权限模型设计——从医疗到金融,每份架构图都标注了MCP协议如何嵌入现有IAM体系
六、立刻能做的四件事
- 检查你的
list_tools实现
用curl发个无权限请求:curl -X POST http://localhost:3000/tools/list_tools,看返回里有没有数据库表名或系统路径。 给所有工具加权限守卫
在每个工具函数开头插入:assert "db.read" in context.permissions, "Permission denied"- 删掉所有
eval()、exec()、getattr(..., user_input)
MCP工具必须是纯函数,输入输出完全可预测。 用Supabase RLS做最后一道闸
即使MCP Server出问题,也让数据库自己拦住非法查询:ALTER TABLE users ENABLE ROW LEVEL SECURITY; CREATE POLICY select_own ON users FOR SELECT USING (auth.uid() = id);