mcpField
S
supabase-audit-realtime

supabase-audit-realtime

github.com/yoanbernabeu安全71529 次安装39 周前更新
代码仓库

关于 supabase-audit-realtime

用于测试 Supabase Realtime WebSocket 频道的安全审计技能。它检查 Postgres Changes、Broadcast 和 Presence 的访问权限,识别未授权订阅或数据暴露,并逐步记录结果与证据。

主要功能

  1. 01枚举可访问的频道
  2. 02测试 Postgres Changes 订阅
  3. 03检查 Broadcast 访问控制
  4. 04检查 Presence 数据暴露
  5. 05逐步记录发现与证据

适用场景

  1. 01检查表变更是否超出 RLS 权限范围而被推送
  2. 02评估 Broadcast 频道的授权机制
  3. 03审查 Presence 共享的敏感信息

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/yoanbernabeu/supabase-pentest-skills --skill supabase-audit-realtime

安装后在 Claude Code 中输入/supabase-audit-realtime确认已加载。

详细介绍

实时频道审计

🔴 严重:必须逐步更新文件

必须边操作边写入上下文文件,不能只在最后写入。

  • 每测试完一个频道后,立即写入 .sb-pentest-context.json
  • 每次订阅测试之前和之后,都要记录到 .sb-pentest-audit.log
  • 不要等技能完成后才更新文件
  • 如果技能崩溃或中断,之前的所有发现都必须已保存

此要求不是可选项。未能逐步写入属于严重错误。

此技能用于测试 Supabase Realtime WebSocket 频道是否存在安全问题。

何时使用此技能

  • 检查 Realtime 频道是否受到妥善保护
  • 检测未经授权的数据流
  • Realtime 用于敏感数据时
  • 作为全面安全审计的一部分

前提条件

  • 已获取 Supabase URL 和 anon key
  • 已完成检测

了解 Supabase Realtime

Supabase Realtime 支持:

wss://[project].supabase.co/realtime/v1/websocket
功能描述
Postgres Changes流式传输数据库变更
Broadcast发布/订阅消息
Presence用户在线状态跟踪

安全模型

Realtime 遵循 RLS 策略:

  • ✅ 如果 RLS 阻止 SELECT,Realtime 就不会流式传输数据
  • ❌ 如果 RLS 允许 SELECT,Realtime 就会流式传输数据
  • ⚠️ 无需 RLS 即可订阅 Broadcast 频道

执行的测试

测试目的
频道枚举查找开放频道
Postgres Changes测试表数据流
Broadcast测试发布/订阅访问权限
Presence测试 Presence 频道访问权限

用法

Realtime 基础审计
Audit Realtime channels on my Supabase project
测试特定功能
Test if Postgres Changes streams sensitive data

输出格式

═══════════════════════════════════════════════════════════
 REALTIME CHANNEL AUDIT
═══════════════════════════════════════════════════════════

 Project: abc123def.supabase.co
 Endpoint: wss://abc123def.supabase.co/realtime/v1/websocket

 ─────────────────────────────────────────────────────────
 Connection Test
 ─────────────────────────────────────────────────────────

 WebSocket Connection: ✅ Established
 Authentication: Anon key accepted
 Protocol: Phoenix channels

 ─────────────────────────────────────────────────────────
 Postgres Changes Test
 ─────────────────────────────────────────────────────────

 Subscribing to table changes with anon key...

 Table: users
 ├── Subscribe: ✅ Subscribed
 ├── INSERT events: 🔴 P0 - RECEIVING ALL NEW USERS
 ├── UPDATE events: 🔴 P0 - RECEIVING ALL UPDATES
 └── DELETE events: 🔴 P0 - RECEIVING ALL DELETES

 Sample Event Received:
 ```json
 {
   "type": "INSERT",
   "table": "users",
   "record": {
     "id": "550e8400-e29b-...",
     "email": "[email protected]",  ← PII STREAMING!
     "name": "New User",
     "created_at": "2025-01-31T10:00:00Z"
   }
 }

发现:🔴 P0 - 未经身份验证就流式传输用户数据! Realtime 的 RLS 可能未正确配置。

表: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": "[email protected]"
}

───────────────────────────────────────────────────────── 在线状态测试 ─────────────────────────────────────────────────────────

频道:online-users ├── 订阅:✅ 成功 ├── 在线状态列表:正在接收所有在线用户 └── 在线用户数:47

在线状态数据示例:

{
  "user_id": "550e8400-...",
  "email": "[email protected]",
  "status": "online",
  "last_seen": "2025-01-31T14:00:00Z"
}

评估:🟠 P1 - 用户在线状态数据已暴露 请考虑是否应该公开电子邮件/用户 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 "Users see only themselves"
      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
    })
    

═══════════════════════════════════════════════════════════


## Realtime Security Model

### Postgres Changes + RLS

```sql
-- This RLS policy applies to Realtime too
CREATE POLICY "Users see own data"
  ON users FOR SELECT
  USING (auth.uid() = id);

-- With this policy:
-- - API SELECT: Only own data
-- - Realtime: Only own data changes
广播安全
-- Realtime authorization (Supabase extension)
-- Add policies to realtime.channels virtual table

-- Only authenticated users can join
CREATE POLICY "Authenticated users join channels"
  ON realtime.channels FOR SELECT
  USING (auth.role() = 'authenticated');

-- Or restrict specific channels
CREATE POLICY "Admin channel for admins"
  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": "All user data streaming without RLS"
      },
      "orders": {
        "subscribed": true,
        "receiving_events": false,
        "severity": null,
        "finding": "Properly protected by RLS"
      }
    },
    "broadcast": {
      "notifications": {
        "accessible": true,
        "severity": "P0",
        "finding": "User notifications exposed"
      },
      "admin": {
        "accessible": true,
        "severity": "P1",
        "finding": "Admin channel publicly accessible"
      }
    },
    "presence": {
      "online-users": {
        "accessible": true,
        "severity": "P1",
        "users_visible": 47,
        "finding": "User presence data exposed"
      }
    }
  }
}

常见 Realtime 问题

问题原因修复方法
正在流式传输所有数据未启用/配置 RLS启用并配置 RLS
广播开放未设置频道授权添加频道策略
在线状态暴露跟踪的数据过多尽量减少跟踪的数据

修复示例

安全的表流式传输
-- Ensure RLS is enabled
ALTER TABLE users ENABLE ROW LEVEL SECURITY;

-- Policy for authenticated users only
CREATE POLICY "Users see own profile" ON users
  FOR SELECT
  USING (auth.uid() = id);

-- Realtime will now only stream changes for the authenticated user's row
安全的广播频道
// Client: Check access before subscribing
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();
}
最少化的在线状态数据
// Before (too much data)
channel.track({
  user_id: userId,
  email: email,
  name: fullName,
  avatar: avatarUrl
});

// After (minimal data)
channel.track({
  online_at: new Date().toISOString()
  // User details fetched separately if needed
});

强制要求:逐步更新上下文文件

⚠️ 此技能必须在执行过程中逐步更新跟踪文件,而不是只在最后更新。

关键规则:边做边写

不要把所有写入操作都留到最后。请改为:

  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:

    [TIMESTAMP] [supabase-audit-realtime] [START] Auditing Realtime channels
    [TIMESTAMP] [supabase-audit-realtime] [FINDING] P0: users table streaming all data
    [TIMESTAMP] [supabase-audit-realtime] [CONTEXT_UPDATED] .sb-pentest-context.json updated
    
  3. 如果文件不存在,请先创建再写入。

无法接受未更新上下文文件的情况。

强制要求:收集证据

📁 证据目录: .sb-pentest-evidence/06-realtime-audit/

需要创建的证据文件
文件内容
websocket-connection.jsonWebSocket 连接测试
postgres-changes/[table].json表订阅结果
broadcast-channels/[channel].json广播频道访问权限
presence-data/[channel].jsonPresence 数据暴露情况
证据格式
{
  "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": "[REDACTED]",
      "email": "[REDACTED]@example.com",
      "name": "[REDACTED]"
    },
    "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 — 纳入最终报告

评论

支持 Markdown