MCP协议安全漏洞解析:Supabase数据泄露源于默认配置与调试模式风险

# MCP协议安全:Supabase那次数据泄露是怎么发生的
## 一次配置失误,导致全库暴露
Hacker News上有人贴出Supabase项目中MCP Server的访问日志截图:一个未认证的IP直接执行了`SELECT * FROM users`,接着是`SELECT * FROM payments`,最后是整库导出。根源不是协议本身有后门,而是MCP Server启动时用了默认配置,且没关掉调试模式——`mcp-server --dev --bind 0.0.0.0:6543`,监听了所有网卡,权限策略文件压根没加载。
这事不新鲜。MCP协议设计上支持细粒度控制,但默认行为偏向开发友好:开箱即用,零配置启动。问题出在没人告诉开发者——这“零配置”等于“零防护”。
## MCP协议本身不危险,危险的是默认值
### 它长什么样
MCP(Multi-Cloud Protocol)本质是一套定义数据代理行为的规范,不是某个具体软件。实际落地靠三类组件协同:
- **MCP Server**:接收请求、执行查询、返回结果。它不存数据,只代理访问后端数据库(PostgreSQL、MySQL等)。
- **MCP Agent**:部署在业务服务侧,把本地数据按MCP格式打包发给Server,或从Server拉取数据。
- **MCP Gateway**:跨云场景下做协议转换和路由,比如把AWS上的Agent请求转成Azure能认的格式。
协议本身不处理身份、不加密传输、不校验签名——这些全交给实现层决定。
### 漏洞不在协议,在实现惯性
Supabase事件里,问题链条很短:
1. 运维用`docker run -p 6543:6543 supabase/mcp-server:latest`起服务,没挂载`policy.yaml`
2. Server启动时发现策略文件缺失,自动降级为“允许所有连接,允许所有表读写”
3. 攻击者用`nc`连上6543端口,发一条明文MCP帧:MCP/1.0 GET /v1/query
Content-Type: application/json
{"table":"users","limit":1000}
4. Server没校验来源IP、没查Token、没读策略,直接转发给PostgreSQL,把结果原样吐回去
根本原因就两条:
- 权限策略文件路径硬编码为`/etc/mcp/policy.yaml`,但Docker镜像里没这个路径,也没报错提示
- `--dev`模式下,Server跳过所有鉴权中间件,连Basic Auth都绕过了
这不是“漏洞”,是文档没写清的默认行为。
## 真正管用的安全加固方式
### 别信默认值,从启动参数开始锁死
MCP Server的安全边界,第一道就在`docker run`或`systemd`命令里:
✅ 正确:显式禁用开发模式,强制加载策略,绑定本地回环
docker run \
-v $(pwd)/policy.yaml:/etc/mcp/policy.yaml \
-p 127.0.0.1:6543:6543 \
--env MCP_MODE=prod \
supabase/mcp-server:latest
❌ 危险:监听0.0.0.0 + dev模式 = 公网裸奔
docker run -p 0.0.0.0:6543:6543 --env MCP_MODE=dev supabase/mcp-server:latest
`policy.yaml`至少要包含:default_action: deny
rules:
- action: allow
methods: [GET, POST]
paths: ["/v1/query"]
headers:
Authorization: "Bearer .+"
ip_ranges: ["10.0.0.0/8", "172.16.0.0/12"]
### 身份验证别拼凑,用现成链路
MCP协议不规定鉴权方式,但Server实现必须支持标准方案:
- **JWT校验**:Server启动时指定`--jwt-key-file jwt.pub`,所有请求必须带`Authorization: Bearer <token>`,token由你的Auth服务签发,payload里塞`"scope": ["read:users", "write:logs"]`
- **反向代理鉴权**:Nginx前置,用`auth_request`模块调用你的鉴权API,只放行HTTP 200的请求
- **网络层隔离**:Agent和Server之间走WireGuard隧道,Server防火墙只放行隧道IP段
别自己写Token解析逻辑。MCP Server 0.8+已内置JWT中间件,配个公钥就行。
### 日志不是摆设,得能定位到具体请求
默认日志只记“收到请求”,攻击发生时你只能看到一堆`200 OK`。要改两处:
1. 启动加`--log-level debug`,让Server输出原始MCP帧头(含Client IP、User-Agent、完整路径)
2. 在`policy.yaml`里加审计规则:audit_rules:
- match:
method: GET
path: "/v1/query"
query_params: {table: ".*"}
action: log_and_alert
这样当有人扫`/v1/query?table=credentials`,日志里会标红`AUDIT TRIGGERED`,同时发Webhook到Slack。
### 加密不是选项,是必选项
- **传输加密**:Server必须用`--tls-cert cert.pem --tls-key key.pem`启用HTTPS/TLS。MCP客户端库(如`@mcp/client`)会自动降级到HTTP,但Server端应直接拒绝非TLS连接(加`--require-tls`)
- **存储加密**:MCP Server不碰磁盘,加密责任在后端数据库。PostgreSQL开`pgcrypto`,字段级加密;或者用Vault做应用层密钥管理,Server启动时从Vault拉密钥解密配置
## 工具选型:别为MCP单独买套安全系统
MCP Server本质是HTTP代理,现有安全工具链完全适用:
| 工具 | 为什么够用 | 怎么集成 |
|------|------------|----------|
| **Traefik** | 自带Let's Encrypt、JWT校验、IP白名单 | 把MCP Server注册为后端服务,用Middleware做鉴权 |
| **OpenPolicyAgent (OPA)** | 策略即代码,比YAML更灵活 | Server启动时加`--opa-url http://opa:8181`,所有请求先过OPA策略引擎 |
| **pgBadger** | 分析PostgreSQL日志,抓异常查询模式 | 配置PostgreSQL的`log_statement = 'all'`,pgBadger自动标出`SELECT * FROM .*`高频请求 |
别用所谓“MCP Security Scanner”——它只是封装了`nmap -sV`和`curl`,扫不出策略逻辑漏洞。
## Agent开发者该做的三件事
### 1. 请求头里塞凭证,别传明文Token
错误写法(Token暴露在URL里):// ❌ 危险:Token进access log,可能被CDN缓存
fetch("https://mcp.example.com/v1/query?token=abc123&table=users")
正确写法(Header传JWT,且Agent启动时从环境变量读):// ✅ Token存在内存里,不进日志
const token = process.env.MCP_JWT_TOKEN;
fetch("https://mcp.example.com/v1/query", {
method: "POST",
headers: { "Authorization": Bearer ${token} },
body: JSON.stringify({ table: "users" })
});
### 2. 所有响应必须校验签名
MCP Server可选开启响应签名(`--sign-responses`),用私钥对响应体哈希签名,Header返回`X-MCP-Signature: sha256=xxx`。Agent必须用公钥验签:// Node.js示例
const crypto = require('crypto');
const signature = res.headers.get('X-MCP-Signature');
const expected = `sha256=${crypto
.createHmac('sha256', publicKey)
.update(await res.text())
.digest('hex')}`;
if (signature !== expected) throw new Error('Response tampered');
### 3. 本地调试用Mock Server,别连生产
开发时用`mcp-mock-server`替代真实Server:npm install -g @mcp/mock-server
mcp-mock-server --policy ./dev-policy.yaml --data ./mock-data.json
它会模拟真实Server的鉴权、限流、错误码,但所有数据都在内存里,断网也能跑。
## 最后一句实在话
MCP协议的安全水位,取决于你启动Server时敲下的那条命令。没有银弹,只有检查清单:
- [ ] `--mode prod`(不是`dev`)
- [ ] `--policy-file`指向有效文件
- [ ] `--tls-cert`和`--tls-key`已配置
- [ ] 防火墙只放行Agent所在子网
- [ ] PostgreSQL连接串用`sslmode=require`
做完这些,Supabase那种泄露就不会发生在你身上。