一区二区三区在线观看视频-精品蜜桃一区二区三区-成人在线视频播放-日日干视频-欧美精品999-国产不卡视频在线观看-欧美精品久久久久久久久-日韩欧美一区在线-国产精品12-国产免费播放-色综合a-亚洲一卡二卡在线-天天操天天碰-jizz俄罗斯-亚洲三级伦理-韩国91视频-免费成人美女女电影-一区不卡在线观看-久久精品久久99-成人福利网站在线观看-国产色视频在线播放-最新日韩视频在线观看-国产一区欧美日韩-屁屁影院一区二区三区-亚洲精选视频在线

?? MCP生態(tài)

MCP協(xié)議安全邊界缺陷分析與Supabase漏洞加固指南

發(fā)布時(shí)間:2026-04-17 分類: MCP生態(tài)
摘要:MCP協(xié)議安全邊界缺陷解析與加固指南:Supabase漏洞啟示錄Supabase漏洞暴露了什么Hacker News上熱議的“Supabase MCP漏洞致全庫SQL裸奔導(dǎo)出”事件,本質(zhì)不是Supabase寫錯(cuò)了代碼,而是MCP Server在默認(rèn)配置下把數(shù)據(jù)庫當(dāng)成了公共讀取器——任何能通過基礎(chǔ)認(rèn)證的用戶,都能直接觸發(fā)/export端點(diǎn),拿到整個(gè)PostgreSQL實(shí)例的SQL dump。這...

封面

MCP協(xié)議安全邊界缺陷解析與加固指南:Supabase漏洞啟示錄

Supabase漏洞暴露了什么

Hacker News上熱議的“Supabase MCP漏洞致全庫SQL裸奔導(dǎo)出”事件,本質(zhì)不是Supabase寫錯(cuò)了代碼,而是MCP Server在默認(rèn)配置下把數(shù)據(jù)庫當(dāng)成了公共讀取器——任何能通過基礎(chǔ)認(rèn)證的用戶,都能直接觸發(fā)/export端點(diǎn),拿到整個(gè)PostgreSQL實(shí)例的SQL dump。

這個(gè)漏洞不依賴SQL注入、不靠服務(wù)端模板渲染,純粹是權(quán)限模型在協(xié)議層塌方的結(jié)果。它提醒我們:當(dāng)AI Agent能自由調(diào)用MCP Server時(shí),協(xié)議本身必須守住第一道門,而不是把所有信任都押在應(yīng)用層的if語句上。

安全邊界在哪塌了

1. 默認(rèn)配置等于開放大門

MCP Server啟動(dòng)時(shí)不強(qiáng)制要求聲明資源策略。它的默認(rèn)行為是:只要JWT簽名有效,就放行所有GET /resources/*POST /actions/*請求。這不是疏忽,是協(xié)議設(shè)計(jì)選擇——但這個(gè)選擇在多租戶場景下立刻失效。

  • 權(quán)限粒度缺失:MCP規(guī)范里沒有resource_idoperation_typecontext_scope等字段的強(qiáng)制校驗(yàn)邏輯。Server實(shí)現(xiàn)通常只校驗(yàn)sub(用戶ID)和exp(過期時(shí)間),剩下的全交給上層應(yīng)用。
  • 認(rèn)證 ≠ 授權(quán):Server完成身份認(rèn)證后,直接把原始請求轉(zhuǎn)發(fā)給后端處理函數(shù)。如果那個(gè)函數(shù)沒做二次鑒權(quán),或者鑒權(quán)邏輯被繞過(比如傳入resource_id=*table_name=public.*),數(shù)據(jù)就裸奔了。

Supabase案例中,攻擊者發(fā)送的請求類似:

POST /v1/export HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

{"format": "sql", "schema": "public", "tables": ["*"]}

MCP Server驗(yàn)證完JWT就轉(zhuǎn)發(fā),而Supabase的導(dǎo)出Handler沒對tables字段做白名單過濾,也沒檢查當(dāng)前用戶是否有pg_dump權(quán)限。

2. 協(xié)議層零鑒權(quán)邏輯

MCP協(xié)議文檔明確將“授權(quán)決策”劃歸應(yīng)用層責(zé)任。這導(dǎo)致兩個(gè)現(xiàn)實(shí)問題:

  • 鑒權(quán)邏輯分散:同一個(gè)資源可能在API網(wǎng)關(guān)、MCP Server中間件、業(yè)務(wù)Handler里被校驗(yàn)三次,也可能一次都沒被校驗(yàn)——取決于開發(fā)者當(dāng)天的心情。
  • 請求可篡改無感知:MCP不強(qiáng)制簽名請求體,也不校驗(yàn)Content-MD5X-Signature頭。攻擊者能輕松修改user_idtenant_idscope等關(guān)鍵參數(shù),而Server照單轉(zhuǎn)發(fā)。

三步堵住協(xié)議層缺口

1. 在MCP Server層做鑒權(quán),別甩鍋給應(yīng)用

RBAC必須下沉到協(xié)議入口。不是加個(gè)中間件,而是讓MCP Server在解析完JWT后,立即查策略引擎,決定是否允許本次請求抵達(dá)業(yè)務(wù)Handler。

# mcp_server/middleware/authz.py
from policy_engine import evaluate

def enforce_mcp_authz(jwt_payload, method, path, body):
    # 提取策略上下文
    context = {
        "user_id": jwt_payload["sub"],
        "role": jwt_payload.get("role", "user"),
        "method": method,
        "path": path,
        "resource": extract_resource_from_path(path),
        "action": infer_action_from_method_and_path(method, path),
        "body_keys": list(body.keys()) if isinstance(body, dict) else []
    }
    
    # 同步調(diào)用策略引擎(不走網(wǎng)絡(luò),本地加載OPA/WASM)
    if not evaluate("mcp_authz.rego", context):
        raise PermissionDenied(f"Policy denied for {context}")

# 在請求路由前調(diào)用
@app.route("/<path:path>", methods=["GET", "POST", "PUT", "DELETE"])
def handle_mcp_request(path):
    jwt = parse_jwt(request.headers.get("Authorization"))
    body = request.get_json() or {}
    enforce_mcp_authz(jwt, request.method, path, body)
    return dispatch_to_handler(path, body)

2. 權(quán)限控制必須精確到操作+資源+上下文

“能訪問用戶表”不等于“能導(dǎo)出用戶表”。權(quán)限模型要區(qū)分:

  • read:user:profile(讀個(gè)人資料)
  • read:table:users(讀users表所有行)
  • export:database:public(導(dǎo)出整個(gè)public schema)
# 權(quán)限檢查邏輯必須嵌入業(yè)務(wù)Handler內(nèi)部,而非僅依賴路由
def export_database_handler(user_id, schema, tables):
    # 檢查用戶是否有該schema的導(dǎo)出權(quán)限
    if not has_permission(user_id, f"export:schema:{schema}"):
        raise Forbidden("Missing export:schema permission")
    
    # 檢查每個(gè)table是否在用戶授權(quán)范圍內(nèi)
    allowed_tables = get_allowed_tables(user_id, schema)
    for table in tables:
        if table != "*" and table not in allowed_tables:
            raise Forbidden(f"Table {table} not in allowed list")
    
    # 執(zhí)行導(dǎo)出(此時(shí)已確保安全)
    return pg_dump(schema, tables)

# 權(quán)限數(shù)據(jù)存在獨(dú)立策略服務(wù)里,不和業(yè)務(wù)DB混用
def get_allowed_tables(user_id, schema):
    return requests.get(
        f"https://policy.internal/allowed-tables?user={user_id}&schema={schema}"
    ).json()

3. Agent調(diào)用必須沙箱化,且沙箱由MCP Server管理

Agent不是可信執(zhí)行體。MCP Server收到/agent/run請求后,不能直接subprocess.run(),而應(yīng):

  • 啟動(dòng)隔離容器(gVisor或Firecracker輕量VM)
  • 掛載只讀的代碼目錄 + 臨時(shí)內(nèi)存盤
  • 設(shè)置rlimit硬限制(CPU 5s、內(nèi)存 256MB、網(wǎng)絡(luò)禁止外連)
  • 超時(shí)強(qiáng)殺,返回SIGKILL狀態(tài)碼而非SIGTERM
# mcp_server/agent_runner.py
import firecracker
from tempfile import mkdtemp

def run_agent_sandboxed(agent_code, timeout=5):
    # 創(chuàng)建臨時(shí)工作區(qū)
    workdir = mkdtemp()
    with open(f"{workdir}/main.py", "w") as f:
        f.write(agent_code)
    
    # 啟動(dòng)Firecracker microVM
    vm = firecracker.MicroVM(
        kernel="/boot/vmlinux",
        initrd="/rootfs.ext4",
        cpu_count=1,
        mem_size_mb=256,
        network="none",  # 禁止網(wǎng)絡(luò)
        drives=[firecracker.Drive(workdir, readonly=True)]
    )
    
    try:
        vm.start()
        result = vm.execute("python3 /mnt/main.py", timeout=timeout)
        return {"status": "success", "output": result.stdout}
    except firecracker.TimeoutError:
        vm.kill()
        return {"status": "timeout", "error": "Execution exceeded 5s"}
    finally:
        vm.cleanup()

安全是可交付的模塊,不是PPT里的形容詞

用戶不會為“高可用”付錢,但會為“導(dǎo)出數(shù)據(jù)前必須二次確認(rèn)+審計(jì)日志+72小時(shí)追溯”付費(fèi)。安全能力直接對應(yīng)三個(gè)變現(xiàn)點(diǎn):

  • 企業(yè)版強(qiáng)制策略引擎:把OPA策略編譯成WASM,在MCP Server內(nèi)聯(lián)執(zhí)行,按策略條數(shù)收費(fèi)
  • 沙箱運(yùn)行時(shí)即服務(wù):按Agent調(diào)用次數(shù)和資源配額計(jì)費(fèi)(如:100次/月免費(fèi),超量0.02美元/次)
  • 合規(guī)審計(jì)包:自動(dòng)生成SOC2、ISO27001所需日志視圖,附帶簽名報(bào)告

這些不是附加功能,是MCP Server的默認(rèn)行為開關(guān)。關(guān)掉它們?可以,但得簽免責(zé)協(xié)議。

下一步:現(xiàn)在就做這五件事

  1. 停用所有*通配符權(quán)限:檢查MCP Server配置,刪掉allow_all_authenticated這類flag
  2. /health端點(diǎn)旁加/policy-status:返回當(dāng)前生效的策略版本、最后更新時(shí)間、未覆蓋路徑列表
  3. 給所有Agent調(diào)用加沙箱包裝器:哪怕只是unshare -r -f --mount-proc也比裸跑強(qiáng)
  4. 重寫導(dǎo)出類API:強(qiáng)制要求tables字段為非空數(shù)組,禁用["*"],改用list_tables接口分頁獲取
  5. 在CI里加策略測試:用真實(shí)JWT token跑curl -X POST /export -d '{"tables":["users"]}',斷言返回403而非200
返回首頁