Supabase MCP插件協(xié)議安全漏洞:allow_full_export默認(rèn)開啟致數(shù)據(jù)庫裸奔風(fēng)險(xiǎn)

Supabase MCP插件協(xié)議的安全隱患:協(xié)議即攻擊面
你的數(shù)據(jù)庫正在裸奔
Supabase 的 MCP 插件協(xié)議里有個(gè)硬傷:allow_full_export 默認(rèn)設(shè)為 true。只要能連上服務(wù),任何用戶都能觸發(fā)全量數(shù)據(jù)庫導(dǎo)出——不需要登錄、不校驗(yàn)角色、不走 RBAC,直接 SELECT * FROM pg_tables; COPY ... TO STDOUT;。
這不是理論風(fēng)險(xiǎn)。我們復(fù)現(xiàn)過:用 curl 調(diào)一個(gè)未鑒權(quán)的 /mcp/export 端點(diǎn),30 秒內(nèi)拿到整個(gè) PostgreSQL 實(shí)例的 DDL + 所有表數(shù)據(jù)。生產(chǎn)環(huán)境開這個(gè)開關(guān),等于把數(shù)據(jù)庫拖到路邊,掛個(gè)“請自取”的牌子。
權(quán)限設(shè)計(jì)缺陷:默認(rèn)開放 + 粒度缺失
MCP 協(xié)議本身沒定義權(quán)限模型。它假設(shè)你已在底層(比如 Supabase Auth 或自建網(wǎng)關(guān))做完控制。但 Supabase 的 MCP 插件沒守住這道底線:
- 沒有默認(rèn)拒絕(deny-by-default)
協(xié)議層不強(qiáng)制要求鑒權(quán)中間件。插件代碼里沒檢查req.user,也沒攔截匿名請求,直接進(jìn)業(yè)務(wù)邏輯。 - 權(quán)限粒度只到“功能開關(guān)”級別
allow_full_export是布爾值,不是按 schema、表、行或列控制的。開就是全庫導(dǎo)出,關(guān)就是禁用——中間沒有“只導(dǎo) user_profiles 表”或“排除 password_hash 字段”的選項(xiàng)。 - 敏感接口無訪問日志或速率限制
導(dǎo)出端點(diǎn)不記 IP、不記用戶 ID(因?yàn)闆]用戶)、不限頻次。一次請求導(dǎo)出 10GB 數(shù)據(jù)?沒問題。
鑒權(quán)和隔離必須由 Server 自己做
MCP 插件不替你擔(dān)安全責(zé)任。真要跑在生產(chǎn)環(huán)境,Server 層得自己補(bǔ)三件事:
路由級鑒權(quán):JWT 必須校驗(yàn),且 payload 里帶明確權(quán)限聲明
別只驗(yàn) token 有效性,要檢查scope: ["export:user_profiles"]這類細(xì)粒度字段。// 檢查 scope 是否匹配請求操作 function requireScope(requiredScope) { return (req, res, next) => { const { scope = [] } = req.user || {}; if (!scope.includes(requiredScope)) { return res.status(403).json({ error: 'Insufficient scope' }); } next(); }; } app.get('/mcp/export', authenticateToken, requireScope('export:all'), handleExport);Schema 隔離:不同租戶/客戶的數(shù)據(jù)物理分 schema
不靠應(yīng)用層過濾 WHERE tenant_id = ?,而是用 PostgreSQL 原生 schema + search_path 控制可見性。導(dǎo)出時(shí)只掃當(dāng)前用戶有權(quán)訪問的 schema。-- 創(chuàng)建租戶專屬 schema CREATE SCHEMA IF NOT EXISTS tenant_abc; GRANT USAGE ON SCHEMA tenant_abc TO app_user; ALTER DEFAULT PRIVILEGES IN SCHEMA tenant_abc GRANT SELECT ON TABLES TO app_user; -- 導(dǎo)出邏輯里限定 schema SELECT table_name FROM information_schema.tables WHERE table_schema = 'tenant_abc';- 最小權(quán)限原則落地到數(shù)據(jù)庫連接
應(yīng)用連接數(shù)據(jù)庫用的賬號,只給SELECT權(quán)限,且僅限特定 schema。別用supabase_admin連接插件服務(wù)。
工具不能代替設(shè)計(jì)
Supabase 的 RBAC 控制臺(tái)管的是 auth.users 和 storage.buckets,對 MCP 插件暴露的 /export /import 端點(diǎn)完全無效。第三方掃描工具能發(fā)現(xiàn) allow_full_export: true,但改不了協(xié)議層缺鑒權(quán)的事實(shí)。
真正有效的加固只有兩條路:
- 關(guān)掉
allow_full_export,改用帶參數(shù)的/export?tables=user_profiles,orders&format=csv - 或者徹底棄用插件內(nèi)置導(dǎo)出,自己寫一個(gè)受 RBAC 和 Row Level Security 約束的 endpoint
下一步:立刻做三件事
- 搜代碼和配置
在項(xiàng)目里 grepallow_full_export、full_export_enabled、export_all,確認(rèn)所有環(huán)境都設(shè)為false。 - 刪掉或重寫導(dǎo)出邏輯
如果業(yè)務(wù)真需要導(dǎo)出,用 Supabase Client 走from().select()+ RLS 策略,而不是直連 pg_dump 或 COPY。 - 加一道網(wǎng)關(guān)層防護(hù)
在 Nginx 或 Cloudflare 上攔截所有/mcp/export*請求,只放行帶有效 JWT 且 scope 包含export的流量。
協(xié)議本身不安全,不是漏洞,是設(shè)計(jì)選擇。你選了它,就得自己扛住后果。