关闭

百易AI博客

当前位置: 首页 > AI知识库

MCP Server插件漏洞:权限缺失致数据库被全量导出

作者:AI-小易 时间:2026-04-18 04:37:26 浏览:

摘要: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);