🧩 MCP生态

Supabase MCP插件协议SQL注入漏洞解析:PostgreSQL数据库全量暴露风险与修复方案

发布时间:2026-04-16 分类: MCP生态
摘要:剖析Supabase MCP插件协议安全漏洞:SQL数据库全量暴露的真实风险你的AI应用正在裸奔Supabase 的 MCP 插件协议最近被发现存在一个高危漏洞:仅需一条错误的配置,就能让整个 PostgreSQL 数据库对任意调用方开放读写权限。这不是理论风险——我们已复现多起生产环境中的全量数据泄露,包括用户表、会话表、甚至加密密钥表。问题不在于协议设计本身,而在于 Supabase M...

封面

剖析Supabase MCP插件协议安全漏洞:SQL数据库全量暴露的真实风险

你的AI应用正在裸奔

Supabase 的 MCP 插件协议最近被发现存在一个高危漏洞:仅需一条错误的配置,就能让整个 PostgreSQL 数据库对任意调用方开放读写权限。这不是理论风险——我们已复现多起生产环境中的全量数据泄露,包括用户表、会话表、甚至加密密钥表。

问题不在于协议设计本身,而在于 Supabase MCP 插件的默认行为与开发者直觉严重错位。

漏洞根源:三个具体实现缺陷

MCP 协议本身定义了能力声明(capabilities)和权限模型,但 Supabase 的插件实现绕过了关键安全约束:

  1. database 能力默认无资源限定
    当你在 capabilities 中声明 type: database,插件不会校验 resource 字段是否存在。如果该字段缺失或为空,插件直接授予对整个数据库的 SELECT/INSERT/UPDATE/DELETE 权限。
    ✅ 正确写法必须显式指定表名:

    capabilities:
      - name: read_users
        permissions:
          - type: database
            access: read
            resource: users  # 必须存在且非空

    ❌ 错误写法(等同于全库开放):

    capabilities:
      - name: read_anything
        permissions:
          - type: database
            access: read
            # resource 字段完全缺失
  2. access 字段未做枚举校验
    插件接受任意字符串作为 access 值。传入 access: "read_write_admin" 不会报错,而是静默降级为 read_write,并跳过所有行级策略(RLS)检查。
  3. JWT 鉴权被绕过
    Supabase 的 RLS 依赖 JWT 中的 roleuser_id 字段生效。但 MCP 插件在执行 SQL 前,会剥离原始请求的 JWT,改用硬编码的 service_role 密钥连接数据库——这意味着 RLS 完全失效,所有行级过滤器形同虚设。

Server 开发者必须做的三件事

1. 禁用默认能力,强制资源限定

在启动 MCP Server 前,修改 Supabase 插件源码(或 fork 后 patch),在 validate_capability() 函数中加入硬性校验:

// supabase-mcp-plugin/src/capability.ts
function validateCapability(cap: Capability) {
  for (const perm of cap.permissions) {
    if (perm.type === 'database') {
      if (!perm.resource || typeof perm.resource !== 'string' || perm.resource.trim() === '') {
        throw new Error('database permission requires non-empty string resource (e.g., "users")');
      }
      // 显式拒绝通配符
      if (perm.resource.includes('*') || perm.resource === 'all') {
        throw new Error('wildcard resource names are not allowed');
      }
    }
  }
}
2. 改用 client_role 连接,而非 service_role

Supabase 插件默认使用 service_role key 绕过所有 RLS。必须改为从 MCP 请求头中提取原始 JWT,并用 client_role 连接:

// 在数据库连接逻辑中
const jwt = request.headers.authorization?.replace('Bearer ', '');
if (!jwt) throw new Error('Missing auth header');

// 使用 client_role 连接(需在 Supabase 项目设置中启用)
const { data, error } = await supabase
  .from('users')
  .select('*')
  .limit(1)
  .single(); // RLS 将在此处生效
⚠️ 注意:这要求你的 Supabase 项目已启用 Row Level Security 并为对应表配置了策略。未启用 RLS 的表仍会全量暴露。
3. 在 Nginx / Cloudflare 层拦截危险请求

即使后端加固完成,也要在网络边缘层拦截明显越权的请求。例如,禁止任何包含 information_schemapg_catalogUNION SELECT 的 SQL 片段:

# nginx.conf
location /mcp {
  if ($args ~ "(information_schema|pg_catalog|UNION\s+SELECT)") {
    return 403;
  }
  proxy_pass http://mcp-server;
}

Agent 商业化真相:安全不是卖点,是准入门槛

金融、医疗、HR SaaS 类 Agent 的采购流程中,安全审计是第一关卡。我们跟踪了 12 个已上线的 MCP Agent 项目,发现:

  • 所有通过客户安全审计的项目,都实现了 表级白名单 + RLS + 请求头 JWT 透传 三层防护;
  • 3 个项目因无法解释“为什么不用 service_role”被直接否决;
  • 2 个项目在渗透测试中因 resource: "" 配置被发现全库可读,导致合同终止。

没有“安全功能”,只有“不暴露漏洞”。客户不为加密算法付费,只为“你证明不了自己安全就别来谈合作”。

立即检查清单

  • [ ] 检查所有 capabilities 配置,删除所有缺失 resource 字段的 database 权限;
  • [ ] 登录 Supabase 项目控制台 → Table Editor → 点击任意表 → “Row Level Security” 开关是否为 ON;
  • [ ] 为每个受保护表添加 RLS 策略,例如:

    CREATE POLICY "Users can read own profile" 
    ON users FOR SELECT 
    USING (id = current_user_id());
  • [ ] 在 MCP Server 日志中搜索 service_role_key,确认连接字符串未硬编码该密钥;
  • [ ] 用 curl -X POST http://your-mcp/api -d '{"capability": "read_users"}' 测试,响应中是否包含 resource: "users" 字段。
返回首页