Supabase MCP安全漏洞详解:allow_unrestricted_queries配置错误致PostgreSQL全库泄露
摘要: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 = trueMCP 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要的是后端可信身份。
复盘那个被拖库的配置
回到开头的事故,真正的问题链是:
supabase.toml中allow_unrestricted_queries = true(默认为false)- 防火墙开放了
6543端口给0.0.0.0/0 - 没启用
enable_rls = true,PostgreSQL的RLS策略形同虚设 - 数据库连接池用的是
postgres超级用户,而非最小权限角色
修复只需三步:
- 改
allow_unrestricted_queries = false - 改
enable_rls = true并绑定策略 - 把
DATABASE_URL里的postgres换成专用角色mcp_reader
别依赖课程,先做这三件事
安全不是学出来的,是改出来的:
- 立刻检查你的
supabase.toml:搜allow_unrestricted_queries和enable_rls,确认值符合生产要求 - 登录数据库执行
\du:看是否存在mcp_*专用角色,且没被赋予SUPERUSER或CREATEDB权限 - 用
nc -zv your-host 6543测试端口:如果公网可连,加防火墙规则或改用VPC内网通信
MCP本身没问题。出问题的是把数据库当玩具配的习惯。