Supabase MCP插件高危SQL泄露風險解析:協議安全與元數據接口防護方案
Supabase MCP插件高危SQL泄露風險解析:協議安全是Agent商業化的生命線
事件回顧:Supabase MCP插件的“裸奔”危機
Supabase 是一個開源的 Firebase 替代方案,提供實時數據庫、身份驗證和 API 自動生成能力。它的 MCP(Multi-Cloud Protocol)插件本意是簡化跨云數據協作,但近期被發現存在嚴重設計缺陷:未校驗的協議調用 + 默認暴露的元數據接口 = 數據庫結構完全可見。
攻擊者無需認證,只需向插件暴露的端點發起 HTTP GET 請求,就能拿到完整的表結構:
GET /v1/metadata/tables響應中包含所有表名、字段名、類型、主鍵、外鍵、索引,甚至注釋。這意味著:
- 攻擊者能精準構造 SQL 注入或越權查詢
- 可批量導出敏感字段(如
users.email,orders.amount) - 為后續橫向移動提供完整攻擊地圖
這個接口在默認配置下開啟,且不校驗來源 IP、不檢查 JWT、不驗證調用方是否已授權訪問目標項目——相當于把數據庫的 ER 圖貼在服務器門口。
風險成因:MCP 協議與 Server 實現的錯位
MCP 協議本身不危險,危險的是實現方式
MCP 是一套定義跨云服務如何交換元數據和執行操作的規范。它要求服務提供統一的 list_tables, get_schema, execute_sql 等方法。問題不在于這些方法存在,而在于:
- 規范未強制要求認證/授權語義(只說“應支持”,沒說“必須校驗”)
- 實現者誤將“協議可調用”等同于“接口可公開”
- 元數據接口被當作調試便利功能,而非生產級敏感端點
Server 開發中的三個具體疏漏
- 元數據接口無訪問邊界
插件把GET /v1/metadata/*路由直接映射到內部 schema 查詢邏輯,中間跳過了項目租戶隔離層。結果是:任意請求都能看到當前數據庫實例下的全部 schema —— 不管該請求聲稱代表哪個 Supabase 項目。 - MCP 調用未綁定會話上下文
MCP 協議消息里帶project_id字段,但插件未校驗該 ID 是否與請求攜帶的 JWT 中聲明的sub或aud匹配。攻擊者可篡改project_id值,讀取其他客戶的表結構。 - 缺乏最小權限原則的默認配置
生產環境默認啟用metadata功能模塊,且未提供一鍵關閉開關。更關鍵的是,沒有區分「開發調試」和「生產運行」兩套配置模式。.env里一行ENABLE_METADATA=false就能阻斷大部分攻擊面,但它不存在。
防御方案:四條可落地的技術動作
1. 強制協議調用綁定可信上下文
MCP 請求必須攜帶有效憑證,并在校驗后與操作上下文強綁定:
// 示例:Express 中間件校驗邏輯
app.use('/v1/mcp', async (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) return res.status(401).end();
const token = authHeader.split(' ')[1];
const payload = verifyJWT(token); // 驗證簽名 & exp & aud
// 關鍵:將 project_id 從 JWT 中提取,而非信任請求 body
req.projectId = payload.aud; // 或 payload.sub,取決于你的租戶模型
next();
});后續所有 MCP 方法(如 list_tables)都必須使用 req.projectId 構造查詢,而不是解析請求體里的 project_id。
2. 元數據接口默認關閉,按需顯式啟用
- 刪除
ENABLE_METADATA=true這類全局開關 - 改為按項目粒度控制:在 Supabase 項目設置中新增「暴露元數據」布爾選項
后端查詢前加檢查:
SELECT enabled FROM projects WHERE id = $1;- 若禁用,直接返回
403 Forbidden,不碰數據庫 schema 表
3. 對元數據做動態脫敏,而非全量透出
即使客戶主動啟用了元數據接口,也不應返回原始 schema。改為:
- 隱藏真實表名/字段名(返回別名或哈希值,僅限調試會話)
- 過濾敏感字段:自動跳過含
email,phone,ssn,password等關鍵詞的列 - 移除注釋、索引詳情、約束條件等非必要信息
// 原始響應(危險)
{
"name": "users",
"columns": [
{ "name": "email", "type": "text", "comment": "user's verified email" }
]
}
// 脫敏后(安全)
{
"name": "t_8a3f",
"columns": [
{ "name": "c_2b9e", "type": "text" }
]
}4. 把安全檢查寫進 CI/CD 流水線
在每次構建 MCP Server 時自動執行:
- 掃描所有路由,標記未加
auth中間件的/v1/metadata/*和/v1/mcp/*端點 → 失敗 - 檢查
docker-compose.yml或 Helm chart 中是否包含ENABLE_METADATA環境變量 → 失敗 - 運行最小化滲透測試腳本,嘗試無認證訪問元數據接口 → 失敗則阻斷發布
# 測試腳本片段
if curl -s -o /dev/null -w "%{http_code}" http://localhost:54321/v1/metadata/tables | grep -q "200"; then
echo "FAIL: metadata endpoint exposed without auth"
exit 1
fi真實案例:一家客服 Agent 公司怎么做
他們用 Supabase MCP 插件連接銀行客戶的數據庫,處理工單分類和退款審批。面對同樣風險,他們做了三件事:
- 砍掉所有通用元數據接口:只保留一個
POST /v1/mcp/validate_query,輸入 SQL 片段,返回「語法合法」或「字段不存在」,不返回任何 schema 信息 - SQL 執行加白名單:
execute_sql方法只允許SELECT,且必須匹配預設模板(如SELECT * FROM tickets WHERE status = ? AND created_at > ?),參數類型和范圍嚴格校驗 - 每條日志帶租戶指紋:Nginx 日志格式中加入
$http_x_project_id,ELK 里可秒級定位某次異常 schema 查詢來自哪個客戶
結果:通過 PCI DSS 二級認證,簽下 7 家區域性銀行,年合同額 520 萬元。客戶明確表示:“不是你們 Agent 多聰明,而是我們敢把生產數據庫交給你。”
下一步:現在就做的四件事
- 立刻檢查你部署的 MCP Server
curl -I https://your-mcp-server/v1/metadata/tables—— 如果返回200 OK,馬上關掉。 - 刪掉所有
ENABLE_*類型的環境變量
改用數據庫字段或配置中心控制功能開關,確保每個項目獨立決策。 - 在
list_tables前加一道租戶校驗 SQL
即使只是SELECT 1 FROM projects WHERE id = $1 AND metadata_enabled = true,也比沒有強。 - 把本文的 CI 檢查腳本加進 pre-commit hook
開發者本地提交代碼前就跑一遍,防患于未然。
安全不是功能列表里最后一項待辦事項。它是你第一個 git commit 就該寫進 README 的那行字:「本服務默認不暴露任何元數據,除非你明確告訴它要暴露給誰。」