supabase-audit-realtime

ๅฎ‰่ฃ…้‡: 89
ๆŽ’ๅ: #8967

ๅฎ‰่ฃ…

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

Realtime Channel Audit ๐Ÿ”ด CRITICAL: PROGRESSIVE FILE UPDATES REQUIRED You MUST write to context files AS YOU GO , not just at the end. Write to .sb-pentest-context.json IMMEDIATELY after each channel tested Log to .sb-pentest-audit.log BEFORE and AFTER each subscription test DO NOT wait until the skill completes to update files If the skill crashes or is interrupted, all prior findings must already be saved This is not optional. Failure to write progressively is a critical error. This skill tests Supabase Realtime WebSocket channels for security issues. When to Use This Skill To check if Realtime channels are properly secured To detect unauthorized data streaming When Realtime is used for sensitive data As part of comprehensive security audit Prerequisites Supabase URL and anon key available Detection completed Understanding Supabase Realtime Supabase Realtime enables: wss://[project].supabase.co/realtime/v1/websocket Feature Description Postgres Changes Stream database changes Broadcast Pub/sub messaging Presence User presence tracking Security Model Realtime respects RLS policies: โœ… If RLS blocks SELECT, Realtime won't stream โŒ If RLS allows SELECT, Realtime streams data โš ๏ธ Broadcast channels can be subscribed without RLS Tests Performed Test Purpose Channel enumeration Find open channels Postgres Changes Test table streaming Broadcast Test pub/sub access Presence Test presence channel access Usage Basic Realtime Audit Audit Realtime channels on my Supabase project Test Specific Feature Test if Postgres Changes streams sensitive data Output Format โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• 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": "newuser@example.com", โ† PII STREAMING! "name": "New User", "created_at": "2025-01-31T10:00:00Z" } } Finding: ๐Ÿ”ด P0 - User data streaming without authentication! RLS may not be properly configured for Realtime. Table: orders โ”œโ”€โ”€ Subscribe: โœ… Subscribed โ”œโ”€โ”€ INSERT events: โŒ Not receiving (RLS working) โ”œโ”€โ”€ UPDATE events: โŒ Not receiving (RLS working) โ””โ”€โ”€ DELETE events: โŒ Not receiving (RLS working) Assessment: โœ… Orders table properly protected. Table: posts โ”œโ”€โ”€ Subscribe: โœ… Subscribed โ”œโ”€โ”€ INSERT events: โœ… Receiving published only โ”œโ”€โ”€ UPDATE events: โœ… Receiving published only โ””โ”€โ”€ DELETE events: โœ… Receiving published only Assessment: โœ… Posts streaming respects RLS (published only). โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Broadcast Channel Test โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Attempting to subscribe to common channel names... Channel: room:lobby โ”œโ”€โ”€ Subscribe: โœ… Success โ”œโ”€โ”€ Messages: Receiving broadcasts โ””โ”€โ”€ Assessment: โ„น๏ธ Open channel (may be intentional) Channel: admin โ”œโ”€โ”€ Subscribe: โœ… Success โ† Should this be public? โ”œโ”€โ”€ Messages: Receiving admin notifications โ””โ”€โ”€ Assessment: ๐ŸŸ  P1 - Admin channel publicly accessible Channel: notifications โ”œโ”€โ”€ Subscribe: โœ… Success โ”œโ”€โ”€ Messages: Receiving user notifications for ALL users! โ””โ”€โ”€ Assessment: ๐Ÿ”ด P0 - User notifications exposed Sample Notification: { "user_id" : "123..." , "type" : "payment_received" , "amount" : 150.00 , "from" : "customer@example.com" } โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Presence Test โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Channel: online-users โ”œโ”€โ”€ Subscribe: โœ… Success โ”œโ”€โ”€ Presence List: Receiving all online users โ””โ”€โ”€ Users Online: 47 Sample Presence Data: { "user_id" : "550e8400-..." , "email" : "user@example.com" , "status" : "online" , "last_seen" : "2025-01-31T14:00:00Z" } Assessment: ๐ŸŸ  P1 - User presence data exposed Consider if email/user_id should be visible. โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Summary โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Postgres Changes: โ”œโ”€โ”€ ๐Ÿ”ด P0: users table streaming all data โ”œโ”€โ”€ โœ… PASS: orders table protected by RLS โ””โ”€โ”€ โœ… PASS: posts table correctly filtered Broadcast: โ”œโ”€โ”€ ๐Ÿ”ด P0: notifications channel exposing user data โ”œโ”€โ”€ ๐ŸŸ  P1: admin channel publicly accessible โ””โ”€โ”€ โ„น๏ธ INFO: lobby channel open (review if intended) Presence: โ””โ”€โ”€ ๐ŸŸ  P1: online-users exposing user details Critical Findings: 2 High Findings: 2 โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• Recommendations โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• FIX USERS TABLE RLS Ensure RLS applies to Realtime: ALTER TABLE users ENABLE ROW LEVEL SECURITY ; CREATE POLICY "Users see only themselves" ON users FOR SELECT USING ( auth . uid ( ) = id ) ; SECURE BROADCAST CHANNELS Use Realtime Authorization: // Require auth for sensitive channels const channel = supabase . channel ( 'admin' , { config : { broadcast : { ack : true } , presence : { key : userId } } } ) // Server-side: validate channel access // Use RLS on realtime.channels table LIMIT PRESENCE DATA Only share necessary information: channel . track ( { online_at : new Date ( ) . toISOString ( ) // Don't include email, user_id unless needed } ) โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

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 Broadcast Security -- 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 ( ) ) ) ; Context Output { "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" } } } } Common Realtime Issues Issue Cause Fix All data streaming RLS not enabled/configured Enable and configure RLS Broadcast open No channel authorization Add channel policies Presence exposed Too much data tracked Minimize tracked data Remediation Examples Secure Table Streaming -- 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 Secure Broadcast Channels // 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 ( ) ; } Minimal Presence Data // 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 } ) ; MANDATORY: Progressive Context File Updates โš ๏ธ This skill MUST update tracking files PROGRESSIVELY during execution, NOT just at the end. Critical Rule: Write As You Go DO NOT batch all writes at the end. Instead: Before testing each channel โ†’ Log the action to .sb-pentest-audit.log After each data exposure found โ†’ Immediately update .sb-pentest-context.json After each subscription test โ†’ Log the result immediately This ensures that if the skill is interrupted, crashes, or times out, all findings up to that point are preserved. Required Actions (Progressive) Update .sb-pentest-context.json with results: { "realtime_audit" : { "timestamp" : "..." , "connection" : "established" , "postgres_changes" : { ... } , "broadcast" : { ... } , "presence" : { ... } } } Log to .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 If files don't exist , create them before writing. FAILURE TO UPDATE CONTEXT FILES IS NOT ACCEPTABLE. MANDATORY: Evidence Collection ๐Ÿ“ Evidence Directory: .sb-pentest-evidence/06-realtime-audit/ Evidence Files to Create File Content websocket-connection.json WebSocket connection test postgres-changes/[table].json Table subscription results broadcast-channels/[channel].json Broadcast channel access presence-data/[channel].json Presence data exposure Evidence Format { "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()" }

โ† ่ฟ”ๅ›žๆŽ’่กŒๆฆœ