Supabase实时通道安全审计Skill supabase-audit-realtime

此技能用于审计 Supabase Realtime WebSocket 通道,检测未授权订阅和数据暴露等安全漏洞。它包括测试 Postgres 更改、广播和在线状态通道,评估行级安全(RLS)策略的有效性,并提供安全修复建议。关键词:Supabase, Realtime, WebSocket, 安全审计, 渗透测试, RLS 策略, 数据保护, 网络安全。

安全审计 0 次安装 0 次浏览 更新于 3/18/2026

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

═══════════════════════════════════════════════════════════ 建议 ═══════════════════════════════════════════════════════════

  1. 修复 USERS 表 RLS 确保 RLS 适用于 Realtime:

    ALTER TABLE users ENABLE ROW LEVEL SECURITY;
    
    CREATE POLICY "用户仅看到自己"
      ON users FOR SELECT
      USING (auth.uid() = id);
    
  2. 保护广播通道 使用 Realtime 授权:

    // 对敏感通道要求认证
    const channel = supabase.channel('admin', {
      config: {
        broadcast: { ack: true },
        presence: { key: userId }
      }
    })
    
    // 服务器端:验证通道访问
    // 在 realtime.channels 表上使用 RLS
    
  3. 限制在线状态数据 仅共享必要信息:

    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()
  // 如果需要,单独获取用户详细信息
});

强制:渐进式上下文文件更新

⚠️ 此技能必须在执行期间渐进式更新跟踪文件,而不是仅在结束时。

关键规则:逐步写入

不要 在结束时批量写入所有内容。相反:

  1. 在测试每个通道之前 → 将操作记录到 .sb-pentest-audit.log
  2. 在每个数据暴露发现后 → 立即更新 .sb-pentest-context.json
  3. 在每个订阅测试后 → 立即记录结果

这确保了如果技能被中断、崩溃或超时,所有先前发现的问题都已保存。

必需操作(渐进式)

  1. 更新 .sb-pentest-context.json 包含结果:

    {
      "realtime_audit": {
        "timestamp": "...",
        "connection": "established",
        "postgres_changes": { ... },
        "broadcast": { ... },
        "presence": { ... }
      }
    }
    
  2. 记录到 .sb-pentest-audit.log

    [时间戳] [supabase-audit-realtime] [开始] 审计实时通道
    [时间戳] [supabase-audit-realtime] [发现] P0:users 表流式传输所有数据
    [时间戳] [supabase-audit-realtime] [上下文已更新] .sb-pentest-context.json 已更新
    
  3. 如果文件不存在,在写入前创建它们。

未能更新上下文文件是不可接受的。

强制:证据收集

📁 证据目录: .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 影响 Realtime
  • supabase-audit-tables-read — API 访问相关
  • supabase-report — 包含在最终报告中