Zum Inhalt springen

Services, Wiki-Artikel, Blog-Beiträge und Glossar-Einträge durchsuchen

↑↓NavigierenEnterÖffnenESCSchließen
Schwachstellenklassen Glossar

WebSocket Security - Sicherheitsrisiken in Echtzeit-Kommunikation

WebSockets ermöglicht bidirektionale Echtzeit-Kommunikation zwischen Browser und Server. Sicherheitsrisiken entstehen durch fehlende Origin-Validierung (Cross-Site WebSocket Hijacking), fehlende Authentifizierung nach Handshake, unverschlüsselte ws:// statt wss://-Verbindungen und fehlende Input-Validierung in WebSocket-Messages. Schutz: Origin-Header validieren, wss:// erzwingen, Token-Authentifizierung beim Handshake.

WebSockets sind ein Kommunikationsprotokoll das eine persistente, bidirektionale Verbindung zwischen Browser und Server ermöglicht. Gegenüber klassischen HTTP-Requests haben WebSockets besondere Sicherheitseigenschaften: Sie sind nicht durch Same-Origin-Policy geschützt wie normale Requests, können durch Proxies modifiziert werden, und der initiale Handshake unterscheidet sich deutlich vom späteren Datenaustausch.

WebSocket-Protokoll und Sicherheitsmodell

Eine WebSocket-Verbindung wird in drei Schritten aufgebaut:

Schritt 1 - HTTP-Upgrade-Request (Handshake):

GET /ws HTTP/1.1
Host: app.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Origin: https://app.example.com  ← WICHTIG! Muss validiert werden!
Cookie: sessionid=abc123          ← Session-Cookie wird mitgesendet!

Schritt 2 - Server-Antwort (101 Switching Protocols):

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Schritt 3 - WebSocket-Frames (bidirektional): Nach dem Handshake läuft die Kommunikation über das Raw WebSocket-Protokoll - nicht mehr über HTTP. Pro Nachricht werden keine HTTP-Header mehr gesendet und Browser senden keine Cookies in WebSocket-Frames. Authentifizierung muss daher über das WebSocket-Protokoll selbst implementiert werden.

Wichtige Unterschiede zu HTTP

MerkmalHTTPWebSocket
Same-Origin-PolicyGreiftGreift nicht bei WebSockets
CORSAccess-Control-Header werden geprüftKein WebSocket-CORS
CSRF-TokensGreifen für HTTP-RequestsGreifen nicht für WebSockets

Diese Unterschiede ermöglichen den spezifischen WebSocket-Angriff: Cross-Site WebSocket Hijacking (CSWSH).

Cross-Site WebSocket Hijacking (CSWSH)

CSWSH ist möglich wenn der Server den Origin-Header nicht validiert und die Authentifizierung über Session-Cookies erfolgt (die beim Handshake automatisch mitgesendet werden).

Angriffsverlauf:

  1. Die Angreifer-Seite evil.com öffnet eine WebSocket-Verbindung zu victim.com
  2. Der Browser sendet automatisch den victim.com-Session-Cookie beim Handshake mit
  3. Der Server authentifiziert die Verbindung (Cookie ist gültig) ohne den Origin zu prüfen
  4. Der Angreifer sendet API-Befehle als eingeloggtes Opfer
  5. Server-Responses werden an die Angreifer-Domain weitergeleitet

Das Opfer muss lediglich evil.com besuchen - zum Beispiel über einen Phishing-Link. Der Angreifer kann dann die vollständige WebSocket-API als Opfer nutzen: Nachrichten lesen, Aktionen ausführen und Daten exfiltrieren.

CSWSH ist mächtiger als normales CSRF, weil nicht nur Seiteneffekte (wie Passwort ändern) möglich sind, sondern der Angreifer auch alle Server-Responses lesen kann - eine bidirektionale Übernahme der Sitzung.

Weitere WebSocket-Schwachstellen

Fehlende Authentifizierung nach Handshake

Wenn Authentifizierung nur beim Verbindungsaufbau stattfindet und nicht pro Nachricht, können Angreifer nach einer Invalidierung des Session-Tokens (Logout in einem anderen Tab) die noch aktive WebSocket-Verbindung weiter nutzen:

ws.onmessage = (msg) => {
  const data = JSON.parse(msg.data);
  // Annahme: Wer verbunden ist, ist authentifiziert
  processCommand(data.command);  // KEINE weitere Auth-Prüfung!
}

WebSocket-Injection

WebSocket-Messages sind genauso anfällig für Injection-Angriffe wie HTTP-Parameter:

// SQL-Injection via WebSocket:
ws.send('{"query": "' + user_input + '"}');
// Payload: {"query": "' OR '1'='1"}
// → Klassische SQLi, nur über WebSocket-Kanal

XSS über WebSocket-Broadcast ist besonders gefährlich: Wenn der Server User-Input unbereinigt an alle verbundenen Clients sendet und der Client den Inhalt unsicher rendert, entsteht ein Stored-XSS-Angriff der alle aktiven Nutzer gleichzeitig betrifft. Gegenmaßnahme: Output-Encoding und textContent statt HTML-Rendering.

Fehlende TLS (ws:// statt wss://)

ws://app.example.com/ws ist eine unverschlüsselte WebSocket-Verbindung. Ein Man-in-the-Middle kann alle übertragenen Daten mitlesen - besonders kritisch bei persistenten, langlebigen WebSocket-Verbindungen. Die Regel ist absolut: immer wss:// (WebSocket Secure über TLS).

Denial-of-Service durch Message-Flooding

WebSocket-Verbindungen sind persistent. Ohne Rate-Limiting kann ein einzelner Client den Server durch schnell gesendete Nachrichten überlasten. Bei Broadcasting-Architekturen entsteht zudem ein Amplification-Faktor: ein Angreifer-Client kann viele andere Clients mit Nachrichten fluten.

Message-Replay

WebSocket-Messages haben keine eingebaute Replay-Protection. Eine in Burp Suite aufgezeichnete Nachricht kann beliebig oft erneut gesendet werden - zum Beispiel eine „Zahlung ausführen”-Nachricht. Schutz: Nonces oder Timestamps in alle Messages einbetten.

Erkennung im Pentest

WebSocket-Testing mit Burp Suite

1. WebSocket-Verbindungen abfangen: Proxy → WebSockets History zeigt alle aufgezeichneten WebSocket-Frames. Einzelne Messages können in Burp Repeater erneut gesendet und modifiziert werden.

2. CSWSH-Test:

  • WebSocket-Handshake in Burp abfangen
  • Origin-Header auf evil.com ändern
  • Handshake erneut senden
  • Verbindung erfolgreich trotz anderer Origin? → CSWSH-Anfälligkeit bestätigt

3. Input-Fuzzing über WebSocket: Burp Intruder kann WebSocket-Messages mit Payloads fuzzen: SQL-Injection-Payloads in String-Felder, XSS-Payloads in User-angezeigte Felder, Path-Traversal in Datei-Referenzen.

4. Authentifizierungs-Test:

  • WebSocket-Verbindung herstellen
  • In einem anderen Browser ausloggen
  • Über die ursprüngliche WebSocket weiter Nachrichten senden
  • Noch authentifiziert? → Session-Binding-Problem

5. TLS-Prüfung: ws:// statt wss:// ist ein sofortiges kritisches Finding. Außerdem prüfen ob ws://-Verbindungen als Downgrade-Fallback erlaubt sind.

Schutzmaßnahmen

1. Origin-Validierung beim Handshake (kritisch!)

// Node.js (ws library):
const wss = new WebSocketServer({ server });
wss.on('connection', (ws, request) => {
  const origin = request.headers.origin;
  const allowedOrigins = ['https://app.example.com', 'https://www.example.com'];
  if (!allowedOrigins.includes(origin)) {
    ws.close(4000, 'Forbidden Origin');
    return;
  }
  // Legitime Verbindung...
});
# Python (websockets):
async def handler(websocket, path):
    origin = websocket.request_headers.get('Origin')
    if origin not in ALLOWED_ORIGINS:
        await websocket.close(code=4000, reason='Forbidden')
        return

2. Token-Authentifizierung beim Handshake

Zusätzlich zum Cookie sollte ein CSRF-Token im Handshake mitgesendet werden:

GET /ws?token=<CSRF-TOKEN>

Der Server validiert den Token aus der Datenbank oder Session. Der Token sollte kurzlebig sein (5 Minuten gültig) und nach dem Verbindungsaufbau invalidiert werden (Einmalnutzung).

3. wss:// erzwingen (Nginx WebSocket-Proxy)

server {
  listen 443 ssl;
  location /ws {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
  }
}

ws://-Verbindungen ablehnen - kein Downgrade-Fallback erlaubt.

4. Rate-Limiting für WebSocket-Messages

let messageCount = 0;
setInterval(() => { messageCount = 0; }, 1000);  // Reset jede Sekunde
ws.on('message', (msg) => {
  messageCount++;
  if (messageCount > 100) {  // Max 100 Messages/Sekunde
    ws.close(4029, 'Too Many Requests');
    return;
  }
  processMessage(msg);
});

5. Input-Validierung in WebSocket-Messages

  • JSON-Schema-Validierung für alle eingehenden Messages
  • Prepared Statements für alle Datenbankzugriffe
  • Output-Encoding für alle ausgehenden Messages
  • textContent statt HTML-Rendering für User-generierte Inhalte
  • Message-Size-Limit (z.B. maximal 64 KB)

6. Idle-Timeout für WebSocket-Verbindungen

Verbindungen nach 30 Minuten Inaktivität schließen und Re-Authentifizierung erzwingen. Dies verhindert Session-Persistenz nach einem Logout und begrenzt das Fenster für Angriffe auf aktive Verbindungen.

Cookielose Analyse via Matomo (selbst gehostet, kein Tracking-Cookie). Datenschutzerklärung