久久一级二级,日本熟人妻中文字幕在线|...久久国产精品-国产精品_日本一区二区三区中文字幕,中文字慕五区,欧美日韩精品一级,9干视频在线,一线在线不卡免费,亚洲天堂久久在线观看,亚洲天堂激情一区,丁香激情四月

?? MCP生態(tài)

Supabase權(quán)限繞過(guò)漏洞解析:MCP協(xié)議如何強(qiáng)化RLS安全與商業(yè)落地

發(fā)布時(shí)間:2026-04-15 分類: MCP生態(tài)
摘要:從 Supabase MCP 全庫(kù) SQL 泄露事件看開(kāi)源項(xiàng)目的安全“幻覺(jué)”——MCP 協(xié)議如何筑起安全防線并實(shí)現(xiàn)商業(yè)價(jià)值事件復(fù)盤:Supabase 的權(quán)限繞過(guò)漏洞Hacker News 上最近熱議的 Supabase “MCP 全庫(kù) SQL 泄露”事件,實(shí)際是一次典型的權(quán)限控制失效。Supabase 并未發(fā)布過(guò)名為 “MCP” 的協(xié)議或功能模塊——這個(gè)標(biāo)簽是誤傳。真實(shí)情況是:攻擊者利用了 ...

封面

從 Supabase MCP 全庫(kù) SQL 泄露事件看開(kāi)源項(xiàng)目的安全“幻覺(jué)”——MCP 協(xié)議如何筑起安全防線并實(shí)現(xiàn)商業(yè)價(jià)值

事件復(fù)盤:Supabase 的權(quán)限繞過(guò)漏洞

Hacker News 上最近熱議的 Supabase “MCP 全庫(kù) SQL 泄露”事件,實(shí)際是一次典型的權(quán)限控制失效。Supabase 并未發(fā)布過(guò)名為 “MCP” 的協(xié)議或功能模塊——這個(gè)標(biāo)簽是誤傳。真實(shí)情況是:攻擊者利用了 pg_net 擴(kuò)展配合自定義函數(shù),構(gòu)造出能繞過(guò) RLS(Row Level Security)策略的 SQL 查詢,最終讀取了本不該可見(jiàn)的全表數(shù)據(jù)。

關(guān)鍵問(wèn)題不在數(shù)據(jù)庫(kù)本身,而在于 Supabase 的默認(rèn)配置和用戶對(duì) RLS 的誤用。RLS 策略只作用于直接 SQL 查詢(如 SELECT * FROM users),但對(duì)通過(guò)函數(shù)返回的結(jié)果集、或 pg_net 這類擴(kuò)展發(fā)起的外部 HTTP 請(qǐng)求,不會(huì)自動(dòng)生效。很多團(tuán)隊(duì)開(kāi)啟 RLS 后就以為萬(wàn)事大吉,卻忽略了策略邊界。

這暴露了一個(gè)常見(jiàn)誤區(qū):把“開(kāi)源”等同于“安全”。代碼可讀不等于邏輯無(wú)缺陷,社區(qū)活躍不等于每個(gè)配置項(xiàng)都被審計(jì)過(guò)。真正的風(fēng)險(xiǎn)往往藏在組合使用、配置偏差和信任鏈斷裂處。

MCP 協(xié)議:Server 端強(qiáng)制校驗(yàn)的落地實(shí)踐

MCP(Multi-Cloud Protocol)不是抽象概念,而是一套明確約束 Server 行為的輕量級(jí)規(guī)范。它不替代數(shù)據(jù)庫(kù)權(quán)限,而是作為應(yīng)用層的第二道閘門——所有數(shù)據(jù)出口必須經(jīng)過(guò)它校驗(yàn),無(wú)論請(qǐng)求來(lái)自 API、函數(shù)、還是擴(kuò)展調(diào)用。

1. 動(dòng)態(tài)策略注入:權(quán)限決策不可繞過(guò)

MCP 要求權(quán)限判定必須發(fā)生在 Server 端,并與具體請(qǐng)求上下文強(qiáng)綁定。策略不能只寫在數(shù)據(jù)庫(kù)里,也不能靠客戶端傳來(lái)的 role 字段做判斷。它強(qiáng)制從可信身份源(如 JWT payload 或 session store)提取角色,并結(jié)合當(dāng)前路由、HTTP 方法、查詢參數(shù)生成實(shí)時(shí)策略。

比如,一個(gè) /api/orders 接口,普通用戶只能查 user_id = ? 的訂單,管理員可查全部,但即使管理員發(fā)來(lái) ?user_id=123,MCP 中間件也會(huì)忽略該參數(shù),改用 WHERE tenant_id = ? AND status != 'deleted' 這類受控條件。

// MCP Server 端動(dòng)態(tài)策略注入示例
const mcp = require('mcp-server');

mcp.use((req, res, next) => {
  const { role, tenant_id } = req.auth; // 來(lái)自 JWT 或 session,非 req.query
  const policy = {
    read:   role === 'admin' ? { where: { tenant_id } } : { where: { tenant_id, user_id: req.auth.user_id } },
    write:  role === 'admin' || role === 'editor',
    delete: role === 'admin'
  };
  req.mcpPolicy = policy;
  next();
});
2. Schema-aware 響應(yīng)過(guò)濾:字段級(jí)裁剪不可跳過(guò)

MCP 規(guī)定響應(yīng)體必須按策略裁剪字段,且裁剪動(dòng)作發(fā)生在序列化之后、發(fā)送之前。這意味著即使數(shù)據(jù)庫(kù)返回了 password_hash 字段,只要策略中未聲明該字段可讀,它就會(huì)被移除——不是靠 ORM 隱藏,也不是靠 SELECT 列表限制,而是對(duì) JSON 響應(yīng)做最終清洗。

// MCP Server 端 schema-aware 響應(yīng)過(guò)濾示例
mcp.use((req, res, next) => {
  const { read } = req.mcpPolicy;
  if (!read) return res.status(403).send('Forbidden');

  // 定義該角色允許返回的字段白名單
  const allowedFields = {
    'user': ['id', 'name', 'email', 'created_at'],
    'admin': ['id', 'name', 'email', 'password_hash', 'last_login']
  }[req.auth.role] || [];

  const originalJson = res.json;
  res.json = function(data) {
    if (Array.isArray(data)) {
      data = data.map(item => pick(item, allowedFields));
    } else {
      data = pick(data, allowedFields);
    }
    return originalJson.call(this, data);
  };

  next();
});

function pick(obj, keys) {
  return Object.fromEntries(keys.map(k => [k, obj[k]]));
}
3. 數(shù)據(jù)沙箱:租戶隔離的最小執(zhí)行單元

MCP 不要求物理隔離,但強(qiáng)制邏輯沙箱。每個(gè)請(qǐng)求必須攜帶 tenant_id(或等效標(biāo)識(shí)),且所有數(shù)據(jù)庫(kù)操作都需顯式注入該 ID。沙箱不是附加功能,而是查詢構(gòu)造器的默認(rèn)行為:

-- ? MCP 合規(guī)寫法:WHERE tenant_id 自動(dòng)注入,不可省略
SELECT id, name FROM products WHERE tenant_id = $1 AND category = $2;

-- ? MCP 拒絕:無(wú) tenant_id 過(guò)濾的查詢
SELECT id, name FROM products WHERE category = $1;

框架層會(huì)攔截任何未帶 tenant_id 的查詢并報(bào)錯(cuò),而不是靜默放行。

MCP Server 開(kāi)發(fā)實(shí)戰(zhàn):防御型編碼范例

以下是一個(gè)生產(chǎn)可用的 MCP Server 示例,它把權(quán)限、過(guò)濾、沙箱三者串成不可拆分的流水線:

const mcp = require('mcp-server');
const express = require('express');
const app = express();

// 1. 認(rèn)證中間件(JWT 驗(yàn)證 + tenant_id 提?。?app.use((req, res, next) => {
  const token = req.headers.authorization?.split(' ')[1];
  if (!token) return res.status(401).send('Unauthorized');
  
  try {
    req.auth = jwt.verify(token, process.env.JWT_SECRET);
  } catch {
    return res.status(401).send('Invalid token');
  }
  next();
});

// 2. MCP 策略注入(基于 auth 信息)
mcp.use((req, res, next) => {
  const { role, tenant_id } = req.auth;
  req.mcpPolicy = {
    read:   role === 'admin' ? { tenant_id } : { tenant_id, user_id: req.auth.user_id },
    write:  ['admin', 'editor'].includes(role),
    sandbox: tenant_id
  };
  next();
});

// 3. MCP 響應(yīng)過(guò)濾(字段白名單)
mcp.use((req, res, next) => {
  const fields = req.auth.role === 'admin' 
    ? ['id', 'email', 'password_hash', 'tenant_id'] 
    : ['id', 'email', 'tenant_id'];
  
  const originalSend = res.send;
  res.send = function(data) {
    if (typeof data === 'object' && data !== null) {
      data = Array.isArray(data) 
        ? data.map(item => pick(item, fields))
        : pick(data, fields);
    }
    return originalSend.call(this, data);
  };
  next();
});

// 4. 數(shù)據(jù)庫(kù)查詢封裝(自動(dòng)注入 sandbox 條件)
function query(sql, params) {
  const tenantId = req.mcpPolicy.sandbox;
  if (!tenantId) throw new Error('Missing tenant_id in MCP policy');
  
  // 自動(dòng)追加 tenant_id 過(guò)濾(支持 WHERE 和 JOIN 場(chǎng)景)
  sql = injectTenantFilter(sql, tenantId);
  return db.query(sql, [tenantId, ...params]);
}

// 路由:所有數(shù)據(jù)出口必須經(jīng)過(guò) MCP 流水線
app.get('/api/users', mcp, async (req, res) => {
  try {
    const users = await query('SELECT * FROM users WHERE status = $1', ['active']);
    res.send(users);
  } catch (err) {
    res.status(500).send('Query failed');
  }
});

app.listen(3000);

MCP 工具評(píng)測(cè):數(shù)據(jù)沙箱實(shí)踐

某 SaaS 電商平臺(tái)用 MCP 工具鏈重構(gòu)了訂單服務(wù)。他們不再依賴 PostgreSQL 的 current_setting('app.tenant_id'),而是將 tenant_id 作為必填 HTTP header,并由 MCP 中間件統(tǒng)一注入到所有查詢中。同時(shí),響應(yīng)過(guò)濾器屏蔽了 payment_method_details、billing_address 等敏感字段,僅對(duì)財(cái)務(wù)角色開(kāi)放。

上線三個(gè)月后:

  • 數(shù)據(jù)泄露類安全告警歸零(此前平均每月 2.3 次)
  • 多租戶數(shù)據(jù)混查事故降為 0(舊架構(gòu)曾因緩存 key 未含 tenant_id 導(dǎo)致)
  • 客戶合規(guī)審計(jì)通過(guò)率從 68% 提升至 100%

工具的價(jià)值不在功能多炫,而在把“必須做”的事變成“不做就報(bào)錯(cuò)”的硬約束。

下一步行動(dòng)

  1. 驗(yàn)證你的權(quán)限模型:檢查所有數(shù)據(jù)庫(kù)查詢是否顯式包含租戶/用戶過(guò)濾條件。沒(méi)有 WHERE tenant_id = ? 的 SQL,一律標(biāo)記為高危。
  2. 在現(xiàn)有 Express/Koa 項(xiàng)目中接入 MCP 中間件:從一個(gè)核心接口開(kāi)始,強(qiáng)制走策略注入 + 響應(yīng)過(guò)濾雙校驗(yàn)。
  3. pg_stat_statements 審計(jì)繞過(guò) RLS 的查詢模式:重點(diǎn)關(guān)注 pg_net、http_get、自定義函數(shù)調(diào)用,它們常是權(quán)限盲區(qū)。
  4. tenant_id 從可選參數(shù)變成請(qǐng)求頭強(qiáng)制字段:用 Nginx 或 API 網(wǎng)關(guān)層攔截缺失 header 的請(qǐng)求,不交給應(yīng)用處理。
返回首頁(yè)
金秀| 石景山区| 枣阳市| 自贡市| 宝应县| 临沂市| 鸡东县| 若羌县| 岳普湖县| 宾川县| 庄河市| 即墨市| 巴东县| 武宣县| 加查县| 那曲县| 察哈| 金平| 石楼县| 顺义区| 紫云| 依兰县| 高碑店市| 和顺县| 大埔区| 广宗县| 宽甸| 于田县| 普安县| 雅安市| 鄄城县| 汶上县| 永定县| 石家庄市| 延庆县| 毕节市| 沂水县| 包头市| 合作市| 临颍县| 大城县|