🧩 MCP生态

Supabase MCP安全漏洞详解:allow_unrestricted_queries配置错误致PostgreSQL全库泄露

发布时间:2026-04-13 分类: MCP生态
摘要:Supabase MCP安全漏洞实战警示:SQL数据库全量泄露是怎么发生的?MCP配置错误导致全库泄露的真实案例Hacker News上有个开发者贴出自己的事故复盘:他用Supabase MCP连接PostgreSQL,只改了一行配置,结果整个数据库被爬空。问题出在supabase.toml里这行:[database] # 错误配置:允许任意客户端执行任意查询 allow_unrestric...

Supabase MCP安全漏洞实战警示:SQL数据库全量泄露是怎么发生的?

MCP配置错误导致全库泄露的真实案例

Hacker News上有个开发者贴出自己的事故复盘:他用Supabase MCP连接PostgreSQL,只改了一行配置,结果整个数据库被爬空。

问题出在supabase.toml里这行:

[database]
# 错误配置:允许任意客户端执行任意查询
allow_unrestricted_queries = true

MCP Server启动后,攻击者直接连上mcp://<host>:6543,发了个SELECT * FROM users;,接着是SELECT * FROM payments;,最后把整库pg_dump走。没有认证绕过,没有0day,纯属配置放开+暴露端口。

MCP协议本身不危险,危险的是默认行为和权限模型

MCP不是新协议,它是Supabase为简化多云数据同步封装的一层gRPC/HTTP网关。它的风险点很具体:

  • 端口暴露即入口:MCP Server默认监听6543(非PostgreSQL原生端口),但只要防火墙放行、没配TLS或IP白名单,就等于把数据库门把手焊死在大街上
  • 权限继承混乱:MCP不自动继承PostgreSQL的行级策略(RLS)。你数据库里开了ENABLE ROW LEVEL SECURITY,MCP照样能绕过——除非显式启用enable_rls = true并绑定策略函数
  • 角色映射缺失:Supabase Auth的auth.uid()在MCP上下文中默认不可用。不手动注入x-user-id头或配置JWT解析,所有请求都以postgres超级用户身份执行

三个必须落地的防护动作

1. 权限隔离:砍掉所有“全局”权限

Supabase MCP不支持GRANT ALL ON DATABASE这种粗粒度授权。必须按表、按列、按操作精确控制:

// 正确做法:创建专用MCP角色,仅授予必要权限
await supabase.rpc('create_mcp_role', {
  role_name: 'mcp_reader',
  schema: 'public',
  table: 'orders',
  columns: ['id', 'status', 'created_at'],
  permissions: ['SELECT']
});

对应SQL:

-- 在PostgreSQL中执行
CREATE ROLE mcp_reader;
GRANT USAGE ON SCHEMA public TO mcp_reader;
GRANT SELECT(id, status, created_at) ON TABLE public.orders TO mcp_reader;
-- 禁止该角色访问敏感表
REVOKE ALL ON TABLE public.payments FROM mcp_reader;

2. 策略校验:用RLS兜底,别信应用层过滤

MCP支持透传RLS策略,但需要显式开启。在supabase.toml中:

[database]
# 必须设为true,否则RLS完全失效
enable_rls = true

[database.rls_policies]
# 绑定策略函数到具体表
"public.orders" = "user_can_read_order"

对应的策略函数:

CREATE POLICY user_can_read_order ON public.orders
  FOR SELECT
  USING (auth.uid() = user_id OR is_admin());

3. RBAC集成:把Auth角色映射到MCP会话

MCP Server启动时需加载Supabase JWT密钥,并在请求头中传递Authorization: Bearer <token>

# 启动命令必须包含JWT验证配置
supabase start --mcp-jwt-secret=your-supabase-jwt-secret

服务端自动解析role字段,映射到PostgreSQL角色。不需要手写setAuth()——那是前端SDK的逻辑,MCP Server要的是后端可信身份。

复盘那个被拖库的配置

回到开头的事故,真正的问题链是:

  1. supabase.tomlallow_unrestricted_queries = true(默认为false
  2. 防火墙开放了6543端口给0.0.0.0/0
  3. 没启用enable_rls = true,PostgreSQL的RLS策略形同虚设
  4. 数据库连接池用的是postgres超级用户,而非最小权限角色

修复只需三步:

  • allow_unrestricted_queries = false
  • enable_rls = true并绑定策略
  • DATABASE_URL里的postgres换成专用角色mcp_reader

别依赖课程,先做这三件事

安全不是学出来的,是改出来的:

  1. 立刻检查你的supabase.toml:搜allow_unrestricted_queriesenable_rls,确认值符合生产要求
  2. 登录数据库执行\du:看是否存在mcp_*专用角色,且没被赋予SUPERUSERCREATEDB权限
  3. nc -zv your-host 6543测试端口:如果公网可连,加防火墙规则或改用VPC内网通信

MCP本身没问题。出问题的是把数据库当玩具配的习惯。

返回首页