摘要:MCP Server插件权限漏洞剖析:MCP协议安全边界实战笔记一、真实问题现场某MCP Server插件上线后不久,客户反馈数据库被全量导出。日志显示,一个未认证的HTTP请求调用了/tools/list_tools端点,返回结果里赫然包含users、payments、auth_sessions等表名,以及字段定义和索引信息。攻击者据此构造SQL注入载荷,绕过应用层直接读取敏感数据。这不是...

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运行时动态发现可用能力,但插件实现中:
元数据不是“辅助信息”,它是攻击链的第一块垫脚石。
RBAC形同虚设
插件声明支持RBAC,但实际只做了两件事:
结果:普通用户调用delete_user时,代码路径和管理员完全相同。
三、Supabase场景下的放大效应
当MCP Server对接Supabase时,问题更致命。Supabase默认开启Row Level Security(RLS),但前提是客户端必须提供有效JWT。而该插件:
Supabase没破防,是MCP Server把门钥匙交给了路人。
四、守住MCP安全边界的实操要点协议规范的关键约束
MCP协议对权限的强制要求就三条,必须落地:
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返回值按角色隔离:
禁用危险反射
禁止工具函数通过getattr()动态调用数据库方法。所有DB操作必须显式声明依赖的表和字段,并在启动时注册白名单:
# 正确:白名单驱动
DB_OPERATIONS = {
"users.read": {"tables": ["users"], "fields": ["id", "email"]},
"payments.write": {"tables": ["payments"], "fields": ["amount", "status"]}
}
商业化案例中的硬性红线
医疗AI Agent的MCP Server部署时,我们强制执行:
这些不是“最佳实践”,是HIPAA合规的底线。
五、的定位
不做MCP协议翻译器,只做三件事:
六、立刻能做的四件事检查你的list_tools实现
用curl发个无权限请求:curl -X POST :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);