MCP協議Server鑒權加固實戰:租戶隔離與SQL執行安全合規方案
摘要:MCP協議實戰解析:Server鑒權加固與合規商業化路徑Supabase漏洞事件:一個真實的權限失控現場Hacker News上那篇題為“Supabase MCP漏洞可導出全庫SQL”的帖子不是假設,是真實發生的越權讀取。攻擊者沒用0day,只靠一個未校驗租戶上下文的/v1/sql端點,加上默認開啟的pg_dump權限,就拿到了整個PostgreSQL實例的結構和數據。問題不在Supabas...
MCP協議實戰解析:Server鑒權加固與合規商業化路徑
Supabase漏洞事件:一個真實的權限失控現場
Hacker News上那篇題為“Supabase MCP漏洞可導出全庫SQL”的帖子不是假設,是真實發生的越權讀取。攻擊者沒用0day,只靠一個未校驗租戶上下文的/v1/sql端點,加上默認開啟的pg_dump權限,就拿到了整個PostgreSQL實例的結構和數據。
問題不在Supabase本身,而在MCP Server實現層:它把“支持MCP協議”等同于“實現了MCP傳輸層”,卻漏掉了最關鍵的租戶隔離邏輯——沒有在SQL執行前綁定current_user、沒做schema級訪問控制、也沒校驗請求頭里的X-MCP-Tenant-ID是否匹配連接池中的實際租戶。
這個漏洞暴露的不是MCP協議設計缺陷,而是Server開發者對協議語義的誤讀:MCP不定義權限模型,它只約定數據如何流動;權限必須由Server自己落地,且必須貫穿到每一行SQL、每一個HTTP響應頭、每一次文件寫入。
MCP Server權限控制的三個落地層
MCP協議文檔里寫的是“Server應確保多租戶數據隔離”,但沒說怎么確保。真正起作用的只有三層:
1. 連接層隔離(最基礎也最容易被繞過)
- 每個租戶必須使用獨立數據庫連接池,不能復用同一連接處理多個租戶請求
- 連接建立時強制執行
SET search_path TO tenant_123, public,而非依賴應用層拼接schema前綴 - 使用
pgbouncer時禁用pool_mode = transaction,改用pool_mode = session
2. 查詢層攔截(核心防線)
# 正確做法:在SQL解析階段注入租戶約束
def execute_sql(tenant_id: str, raw_sql: str) -> List[dict]:
# 1. 攔截DDL(禁止租戶創建新schema)
if re.search(r'\b(CREATE|ALTER|DROP)\s+(SCHEMA|DATABASE)\b', raw_sql, re.I):
raise PermissionError("Tenant not allowed to manage schemas")
# 2. 自動重寫SELECT語句,添加租戶過濾
if raw_sql.strip().upper().startswith('SELECT'):
# 解析AST,找到FROM子句,對每個表注入WHERE tenant_id = ?
rewritten = inject_tenant_filter(raw_sql, tenant_id)
return run_query(rewritten, tenant_id)
# 3. 其他語句直接校驗權限位
if not has_permission(tenant_id, 'sql_exec'):
raise PermissionError("SQL execution denied for tenant")3. 響應層凈化(最后一道閘)
- 所有返回的JSON響應必須經過
sanitize_response()過濾,移除pg_stat_activity等系統視圖中暴露的進程信息 - 文件導出(如CSV/SQL dump)必須用臨時租戶專用目錄,路徑格式為
/tmp/tenant_{id}_{uuid}/ - 錯誤消息禁用
debug=True,避免泄露表名、字段名、索引名
鑒權加固:從補丁到架構
Supabase事件后,我們重構了MCP Server的鑒權鏈,不再依賴單一Token校驗,而是構建三級校驗:
- 傳輸層校驗:驗證
Authorization: Bearer <JWT>簽名與有效期 - 協議層校驗:檢查
X-MCP-Request-ID是否在白名單內(防重放),X-MCP-Client-Version是否兼容 - 業務層校驗:根據
X-MCP-Tenant-ID查租戶策略表,確認本次請求的操作類型(sql_exec,file_read,config_update)是否被授權
關鍵改動在tenant_policy表結構:
CREATE TABLE tenant_policy (
id SERIAL PRIMARY KEY,
tenant_id TEXT NOT NULL,
operation TEXT NOT NULL CHECK (operation IN ('sql_exec', 'file_read', 'file_write', 'config_update')),
allowed BOOLEAN DEFAULT true,
max_rows INTEGER DEFAULT 10000,
timeout_ms INTEGER DEFAULT 5000,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 策略生效示例
INSERT INTO tenant_policy (tenant_id, operation, allowed, max_rows)
VALUES ('acme-corp', 'sql_exec', true, 50000);每次SQL執行前,查詢該表并動態設置statement_timeout和LIMIT:
-- 在連接中執行
SET statement_timeout = 5000;
-- 在SQL末尾自動追加
-- LIMIT 50000商業化落地:一個Agent項目的硬核變現路徑
我們做的跨云數據同步Agent,上線6個月后開始收費。沒走“免費版限功能”路線,而是用安全能力倒逼付費:
收費錨點直指合規痛點
| 功能 | 免費版 | 付費版($299/月) |
|---|---|---|
| 租戶數據隔離 | 進程級隔離 | 數據庫實例級隔離 |
| SQL審計日志 | 僅記錄成功操作 | 記錄完整SQL+參數+執行計劃 |
| 敏感字段脫敏 | 不支持 | 自動識別PII字段并AES加密 |
| 合規報告 | 無 | 自動生成SOC2/ISO27001模板 |
客戶采購決策不是因為“功能多”,而是因為法務部明確要求:“生產環境必須滿足租戶間數據庫實例隔離”。
關鍵轉化動作
定價頁不寫功能列表,寫合規聲明:
“企業版部署即滿足GDPR第32條‘適當技術措施’要求,所有租戶數據存儲于物理隔離的AWS RDS實例,VPC間無網絡互通?!?/blockquote>
- 免費試用限制真實場景:
免費用戶只能連接本地PostgreSQL,無法添加AWS RDS或Cloud SQL連接——讓客戶在第一天就意識到“云環境需要企業版”。 - 銷售話術聚焦故障成本:
“一次租戶數據泄露的平均修復成本是$380萬(IBM《2023數據泄露成本報告》)。企業版隔離方案的成本,不到一次事故的0.1%?!?/li>
下一步:把鑒權變成產品能力
別再把權限控制當成安全團隊的補丁任務。把它做成可配置、可審計、可計費的產品模塊:
- 在管理后臺開放
tenant_policy編輯界面,讓客戶自行開關file_read權限 - 將SQL審計日志接入客戶現有SIEM系統(Splunk/Sentinel),作為增值集成項收費
- 對高頻查詢自動觸發
EXPLAIN ANALYZE,生成性能報告——按報告份數收取分析費
真正的商業化,始于把安全約束翻譯成客戶愿意付費的確定性。