name: supabase-audit-realtime description: 测试 Supabase Realtime WebSocket 通道的未授权订阅和数据暴露。
实时通道审计
🔴 关键:需要渐进式文件更新
您必须 逐步 写入上下文文件,而不是仅在结束时写入。
- 在测试每个通道后 立即 写入
.sb-pentest-context.json- 在每个订阅测试 之前和之后 记录到
.sb-pentest-audit.log- 不要 等到技能完成后再更新文件
- 如果技能崩溃或被中断,所有先前发现的问题必须已保存
这不是可选的。未能逐步写入是严重错误。
此技能测试 Supabase Realtime WebSocket 通道的安全问题。
何时使用此技能
- 检查实时通道是否得到适当保护
- 检测未授权的数据流
- 当实时通道用于敏感数据时
- 作为全面安全审计的一部分
前提条件
- Supabase URL 和 anon 密钥可用
- 检测已完成
了解 Supabase Realtime
Supabase Realtime 支持:
wss://[project].supabase.co/realtime/v1/websocket
| 功能 | 描述 |
|---|---|
| Postgres 更改 | 流式传输数据库更改 |
| 广播 | 发布/订阅消息传递 |
| 在线状态 | 用户在线状态跟踪 |
安全模型
Realtime 尊重 RLS 策略:
- ✅ 如果 RLS 阻止 SELECT,Realtime 不会流式传输
- ❌ 如果 RLS 允许 SELECT,Realtime 流式传输数据
- ⚠️ 广播通道可以在没有 RLS 的情况下订阅
执行的测试
| 测试 | 目的 |
|---|---|
| 通道枚举 | 查找开放通道 |
| Postgres 更改 | 测试表流式传输 |
| 广播 | 测试发布/订阅访问 |
| 在线状态 | 测试在线状态通道访问 |
使用方法
基本实时审计
在我的 Supabase 项目上审计实时通道
测试特定功能
测试 Postgres 更改是否流式传输敏感数据
输出格式
═══════════════════════════════════════════════════════════
实时通道审计
═══════════════════════════════════════════════════════════
项目:abc123def.supabase.co
端点:wss://abc123def.supabase.co/realtime/v1/websocket
─────────────────────────────────────────────────────────
连接测试
─────────────────────────────────────────────────────────
WebSocket 连接:✅ 已建立
认证:Anon 密钥已接受
协议:Phoenix 通道
─────────────────────────────────────────────────────────
Postgres 更改测试
─────────────────────────────────────────────────────────
使用 anon 密钥订阅表更改...
表:users
├── 订阅:✅ 已订阅
├── INSERT 事件:🔴 P0 - 接收所有新用户
├── UPDATE 事件:🔴 P0 - 接收所有更新
└── DELETE 事件:🔴 P0 - 接收所有删除
接收的示例事件:
```json
{
"type": "INSERT",
"table": "users",
"record": {
"id": "550e8400-e29b-...",
"email": "newuser@example.com", ← PII 流式传输!
"name": "新用户",
"created_at": "2025-01-31T10:00:00Z"
}
}
发现:🔴 P0 - 用户数据在未经认证的情况下流式传输! RLS 可能未针对 Realtime 正确配置。
表:orders ├── 订阅:✅ 已订阅 ├── INSERT 事件:❌ 未接收(RLS 工作) ├── UPDATE 事件:❌ 未接收(RLS 工作) └── DELETE 事件:❌ 未接收(RLS 工作)
评估:✅ orders 表得到适当保护。
表:posts ├── 订阅:✅ 已订阅 ├── INSERT 事件:✅ 仅接收已发布内容 ├── UPDATE 事件:✅ 仅接收已发布内容 └── DELETE 事件:✅ 仅接收已发布内容
评估:✅ posts 流式传输尊重 RLS(仅已发布)。
───────────────────────────────────────────────────────── 广播通道测试 ─────────────────────────────────────────────────────────
尝试订阅常见通道名称…
通道:room:lobby ├── 订阅:✅ 成功 ├── 消息:接收广播 └── 评估:ℹ️ 开放通道(可能是有意的)
通道:admin ├── 订阅:✅ 成功 ← 这应该是公开的吗? ├── 消息:接收管理员通知 └── 评估:🟠 P1 - 管理员通道公开可访问
通道:notifications ├── 订阅:✅ 成功 ├── 消息:接收所有用户的通知! └── 评估:🔴 P0 - 用户通知暴露
示例通知:
{
"user_id": "123...",
"type": "payment_received",
"amount": 150.00,
"from": "customer@example.com"
}
───────────────────────────────────────────────────────── 在线状态测试 ─────────────────────────────────────────────────────────
通道:online-users ├── 订阅:✅ 成功 ├── 在线列表:接收所有在线用户 └── 在线用户:47
示例在线状态数据:
{
"user_id": "550e8400-...",
"email": "user@example.com",
"status": "online",
"last_seen": "2025-01-31T14:00:00Z"
}
评估:🟠 P1 - 用户在线状态数据暴露 考虑是否应显示 email/user_id。
───────────────────────────────────────────────────────── 摘要 ─────────────────────────────────────────────────────────
Postgres 更改: ├── 🔴 P0:users 表流式传输所有数据 ├── ✅ 通过:orders 表受 RLS 保护 └── ✅ 通过:posts 表正确过滤
广播: ├── 🔴 P0:notifications 通道暴露用户数据 ├── 🟠 P1:admin 通道公开可访问 └── ℹ️ 信息:lobby 通道开放(检查是否有意)
在线状态: └── 🟠 P1:online-users 暴露用户详细信息
关键发现:2 高发现:2
═══════════════════════════════════════════════════════════ 建议 ═══════════════════════════════════════════════════════════
-
修复 USERS 表 RLS 确保 RLS 适用于 Realtime:
ALTER TABLE users ENABLE ROW LEVEL SECURITY; CREATE POLICY "用户仅看到自己" ON users FOR SELECT USING (auth.uid() = id); -
保护广播通道 使用 Realtime 授权:
// 对敏感通道要求认证 const channel = supabase.channel('admin', { config: { broadcast: { ack: true }, presence: { key: userId } } }) // 服务器端:验证通道访问 // 在 realtime.channels 表上使用 RLS -
限制在线状态数据 仅共享必要信息:
channel.track({ online_at: new Date().toISOString() // 除非需要,否则不要包含 email、user_id })
═══════════════════════════════════════════════════════════
## 实时安全模型
### Postgres 更改 + RLS
```sql
-- 此 RLS 策略也适用于 Realtime
CREATE POLICY "用户看到自己的数据"
ON users FOR SELECT
USING (auth.uid() = id);
-- 使用此策略:
-- - API SELECT:仅自己的数据
-- - Realtime:仅自己的数据更改
广播安全
-- Realtime 授权(Supabase 扩展)
-- 将策略添加到 realtime.channels 虚拟表
-- 仅认证用户可以加入
CREATE POLICY "认证用户加入通道"
ON realtime.channels FOR SELECT
USING (auth.role() = 'authenticated');
-- 或限制特定通道
CREATE POLICY "管理员通道仅管理员使用"
ON realtime.channels FOR SELECT
USING (
name != 'admin' OR
(SELECT is_admin FROM profiles WHERE id = auth.uid())
);
上下文输出
{
"realtime_audit": {
"timestamp": "2025-01-31T14:00:00Z",
"connection": "established",
"postgres_changes": {
"users": {
"subscribed": true,
"receiving_events": true,
"severity": "P0",
"finding": "所有用户数据在无 RLS 的情况下流式传输"
},
"orders": {
"subscribed": true,
"receiving_events": false,
"severity": null,
"finding": "受 RLS 适当保护"
}
},
"broadcast": {
"notifications": {
"accessible": true,
"severity": "P0",
"finding": "用户通知暴露"
},
"admin": {
"accessible": true,
"severity": "P1",
"finding": "管理员通道公开可访问"
}
},
"presence": {
"online-users": {
"accessible": true,
"severity": "P1",
"users_visible": 47,
"finding": "用户在线状态数据暴露"
}
}
}
}
常见实时问题
| 问题 | 原因 | 修复 |
|---|---|---|
| 所有数据流式传输 | RLS 未启用/配置 | 启用和配置 RLS |
| 广播开放 | 无通道授权 | 添加通道策略 |
| 在线状态暴露 | 跟踪数据过多 | 最小化跟踪数据 |
修复示例
保护表流式传输
-- 确保 RLS 已启用
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
-- 仅认证用户的策略
CREATE POLICY "用户看到自己的个人资料" ON users
FOR SELECT
USING (auth.uid() = id);
-- Realtime 现在将仅流式传输认证用户行的更改
保护广播通道
// 客户端:在订阅前检查访问
const { data: canAccess } = await supabase
.from('channel_access')
.select('*')
.eq('channel', 'admin')
.eq('user_id', userId)
.single();
if (canAccess) {
const channel = supabase.channel('admin');
channel.subscribe();
}
最小在线状态数据
// 之前(数据过多)
channel.track({
user_id: userId,
email: email,
name: fullName,
avatar: avatarUrl
});
// 之后(最小数据)
channel.track({
online_at: new Date().toISOString()
// 如果需要,单独获取用户详细信息
});
强制:渐进式上下文文件更新
⚠️ 此技能必须在执行期间渐进式更新跟踪文件,而不是仅在结束时。
关键规则:逐步写入
不要 在结束时批量写入所有内容。相反:
- 在测试每个通道之前 → 将操作记录到
.sb-pentest-audit.log - 在每个数据暴露发现后 → 立即更新
.sb-pentest-context.json - 在每个订阅测试后 → 立即记录结果
这确保了如果技能被中断、崩溃或超时,所有先前发现的问题都已保存。
必需操作(渐进式)
-
更新
.sb-pentest-context.json包含结果:{ "realtime_audit": { "timestamp": "...", "connection": "established", "postgres_changes": { ... }, "broadcast": { ... }, "presence": { ... } } } -
记录到
.sb-pentest-audit.log:[时间戳] [supabase-audit-realtime] [开始] 审计实时通道 [时间戳] [supabase-audit-realtime] [发现] P0:users 表流式传输所有数据 [时间戳] [supabase-audit-realtime] [上下文已更新] .sb-pentest-context.json 已更新 -
如果文件不存在,在写入前创建它们。
未能更新上下文文件是不可接受的。
强制:证据收集
📁 证据目录: .sb-pentest-evidence/06-realtime-audit/
要创建的证据文件
| 文件 | 内容 |
|---|---|
websocket-connection.json |
WebSocket 连接测试 |
postgres-changes/[table].json |
表订阅结果 |
broadcast-channels/[channel].json |
广播通道访问 |
presence-data/[channel].json |
在线状态数据暴露 |
证据格式
{
"evidence_id": "RT-001",
"timestamp": "2025-01-31T11:05:00Z",
"category": "realtime-audit",
"type": "postgres_changes",
"severity": "P0",
"table": "users",
"subscription_test": {
"channel": "realtime:public:users",
"subscribed": true,
"events_received": true
},
"sample_event": {
"type": "INSERT",
"table": "users",
"record": {
"id": "[已编辑]",
"email": "[已编辑]@example.com",
"name": "[已编辑]"
},
"redacted": true
},
"impact": {
"pii_streaming": true,
"affected_columns": ["email", "name"],
"rls_bypass": true
},
"websocket_url": "wss://abc123def.supabase.co/realtime/v1/websocket",
"reproduction_code": "const channel = supabase.channel('realtime:public:users').on('postgres_changes', { event: '*', schema: 'public', table: 'users' }, (payload) => console.log(payload)).subscribe()"
}
相关技能
supabase-audit-rls— RLS 影响 Realtimesupabase-audit-tables-read— API 访问相关supabase-report— 包含在最终报告中