Supabase MCP插件安全漏洞分析:SQL权限校验缺失与Server加固实践

Supabase MCP插件安全漏洞分析:协议缺陷与Server加固实践
漏洞本质:权限校验完全缺失
Supabase MCP插件暴露了一个关键问题:它把MCP请求当作可信输入直接执行,跳过了所有权限检查。任何能连上MCP Server的Agent,都能调用sql_query工具并传入任意SQL——包括SELECT * FROM auth.users或DROP TABLE public.products。这不是配置疏忽,而是协议实现层面的空白。
这个漏洞和Supabase本身无关。Supabase的Row Level Security(RLS)依然生效,但MCP插件绕过了整个RLS链路,直连PostgreSQL后端。结果就是:Agent权限模型在MCP层彻底失效。
MCP协议中本该存在的三道防线
MCP协议本身不强制权限控制,但它定义了三个可落地的控制点。插件没用它们,才导致全线失守。
1. Capability声明必须绑定到具体操作
Capability不是装饰性字段。它应该明确列出Agent被允许调用的工具名和参数约束。例如:
{
"capabilities": [
{
"name": "sql_query",
"parameters": {
"table": ["users", "profiles"],
"allowed_operations": ["SELECT"]
}
}
]
}如果Agent声明里没包含sql_query,Server就该拒绝该请求,而不是放行后才查权限。
2. Scope必须限制数据边界
Scope不是模糊的“数据库访问”,而是精确到表、字段、甚至WHERE条件。比如一个客服Agent的Scope应类似:
"scope": {
"tables": ["public.users"],
"fields": ["id", "email", "created_at"],
"where_clause": "status = 'active'"
}MCP Server收到sql_query时,必须解析SQL,提取目标表和字段,再比对Scope。SELECT * FROM users这种全量查询,在Scope含fields: ["email"]时就应该被拦截。
3. Resource-level ACL需在Server端强制执行
ACL不能只存在文档里。MCP Server启动时,必须加载一份资源策略表,并在每次请求前查表:
| Resource | Agent ID | Allowed Actions | Conditions |
|---|---|---|---|
public.users | agent-1 | SELECT | WHERE role = 'admin' |
public.posts | agent-2 | INSERT | CHECK (length(content) < 500) |
没有这张表,就没有真正的Resource-level ACL。
漏洞复现与影响范围
攻击者只需构造一个合法MCP请求包:
{
"type": "tool_call",
"tool": "sql_query",
"arguments": {
"query": "COPY (SELECT * FROM auth.users) TO '/tmp/dump.csv' WITH CSV"
}
}只要MCP Server监听公网且未设网络层防护,这条请求就能导出全部用户凭证。我们实测发现,该插件默认启用pg_read_server_files扩展,使得文件写入成为可能——这已超出数据泄露,进入系统接管风险。
更隐蔽的问题是:漏洞影响所有基于该插件构建的Agent。即使你给Agent配了严格Capability,只要插件代码没读取这些字段,一切声明都形同虚设。
Server加固:四步落地
1. 在入口处硬性校验Capability
不要等请求进到业务逻辑才检查。MCP Server的路由层就要做两件事:
- 解析Agent的Capability声明(从JWT claim、注册元数据或会话上下文获取)
- 对比当前请求的
tool名是否在声明列表中
function validateToolCall(agentCaps, toolName) {
const cap = agentCaps.find(c => c.name === toolName);
if (!cap) throw new PermissionError(`Tool '${toolName}' not allowed`);
// 还可校验参数结构,如禁止传入DROP语句
return cap;
}
// 在Express中间件中调用
app.post('/mcp', (req, res) => {
const { tool, arguments } = req.body;
const caps = getAgentCapabilities(req.headers.authorization);
validateToolCall(caps, tool); // 拦截点在此
handleTool(tool, arguments);
});2. SQL解析器必须嵌入Scope检查
别依赖开发者手写安全SQL。所有sql_query调用前,必须过一遍轻量SQL解析器(如sql-parser-js),提取AST中的table, columns, where节点,再与Scope比对。
重点拦截:
SELECT *(除非Scope显式声明fields: ["*"])- 跨Schema查询(如
SELECT * FROM auth.users) INSERT/UPDATE/DELETE无WHERE条件COPY,CREATE FUNCTION,DO $$等高危语法
3. 协议层加签与限流
MCP本身不加密,但Server可以要求:
- 所有请求带HMAC签名(密钥由Agent注册时分配)
- 每个Agent每分钟最多10次
sql_query调用 - 连续3次失败请求后,临时封禁该Agent ID 5分钟
签名验证示例:
const crypto = require('crypto');
function verifySignature(req, secret) {
const signature = req.headers['x-mcp-signature'];
const body = JSON.stringify(req.body);
const expected = crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
return signature === expected;
}4. 审计日志必须包含可追溯字段
每条MCP请求日志至少记录:
- Agent ID(非用户名)
- 请求时间戳(ISO 8601)
- 工具名 + 参数摘要(如
sql_query: SELECT id,email FROM users LIMIT 10) - 执行耗时 + 返回行数
- 是否触发权限拒绝(标记为
DENIED)
日志不存本地磁盘,直接发到Loki或Datadog。拒绝事件必须触发PagerDuty告警。
合规Agent的务实变现路径
安全加固不是PPT方案,而是可交付的工程能力。合规Agent能做的不是卖“安全咨询”,而是提供以下可集成模块:
1. MCP ACL SDK
一个NPM包,封装上述四步加固逻辑:
npm install @compliant/mcp-acl开箱即用的Express中间件 + Supabase客户端适配器 + 预置SQL解析规则。企业只需改两行代码:
const { aclMiddleware } = require('@compliant/mcp-acl');
app.use(aclMiddleware({ supabaseUrl, supabaseAnonKey }));2. Scope策略生成器
CLI工具,根据Supabase RLS策略自动生成MCP Scope:
npx @compliant/mcp-scope-gen \
--supabase-url https://xxx.supabase.co \
--anon-key ey... \
--agent-id support-agent输出JSON格式Scope,直接注入Agent注册流程。
3. 实时审计仪表盘
部署一个轻量Dashboard,连接企业的MCP日志流,实时显示:
- 每个Agent的请求成功率/拒绝率
- 被拦截的TOP 5高危SQL模式(如
SELECT * FROM auth.*) - 新增Agent的Capability变更审计
不卖License,按Agent数量月付($49/Agent/月)。
4. MCP安全测试套件
开源的mcp-pentest工具,模拟攻击者行为:
# 测试是否允许跨表查询
mcp-pentest --url https://mcp.example.com --agent agent-1 --test cross-table
# 测试是否拦截危险函数
mcp-pentest --url https://mcp.example.com --agent agent-1 --test pg_read_file报告直接输出Markdown,附修复建议链接。
现在该做什么
如果你正在用Supabase MCP插件:
- 立刻停用公网访问:用Cloudflare Tunnel或VPC Peering隔离MCP Server
- 检查所有Agent注册代码:确认Capability声明已写入,并非空数组
- 部署SQL解析中间件:哪怕先用正则粗筛
SELECT \*和DROP - 打开PostgreSQL日志:设置
log_statement = 'mod',监控异常大查询 - 删掉
pg_read_server_files扩展:除非业务强依赖,否则直接REVOKE ALL ON FUNCTION pg_read_file FROM PUBLIC;
安全不是功能列表里的最后一项。当Agent能执行任意SQL时,数据库就不再是你的。