FlawOpen

flawopen.com/Teardowns/cve-2026-71416-headroom-websocket-origin-hijacking

● CVE-2026-71416 · CVSS 8.8 · Hoch
Sicherheitsforschung · FlawOpen

CVE-2026-71416: Headroom-WebSocket-Origin-Hijacking

Headroom prüfte WebSocket-Origin nicht; eine besuchte Seite konnte LLM-Anfragen über den Proxy senden. Aktualisieren Sie auf 0.35.0+.

💡 Einfache Erklärung (ELI5)

Ein Empfang lässt alle an einen privaten Schalter, weil der Besucherausweis ungeprüft bleibt. Origin ist dieser Ausweis: Der Proxy muss ihn prüfen, bevor er einen Kanal öffnet, der den gespeicherten API-Schlüssel verwenden kann.

Kernkonzepte & Begriffe

WebSocket
Dauerhafte bidirektionale Verbindung, die mit einer HTTP-Upgrade-Anfrage beginnt und Nachrichten in beide Richtungen überträgt.
Origin-Header
Vom Browser gesendetes Schema, Host und Port der Seite, die eine Anfrage ausgelöst hat. Beim WebSocket-Handshake muss der Server eine eigene Vertrauensrichtlinie anwenden; CORS-Header ersetzen diese Prüfung nicht.
CSWSH
Cross-Site WebSocket Hijacking: Eine nicht vertrauenswürdige Seite öffnet einen WebSocket zu einem erreichbaren Dienst, der den Ursprung nicht prüft.
Umgebungsberechtigung
Ein Geheimnis, das dem Prozess bereits zur Verfügung steht, etwa ein API-Schlüssel in einer Umgebungsvariable, und Anfragen ohne eigene Client-Anmeldedaten authentifizieren kann.
CWE-1385
Fehlende Origin-Prüfung bei WebSockets: Der Endpunkt prüft nicht, ob der Kommunikationsursprung zugelassen ist.

Framework-Zuordnungen

Ursachenanalyse

Headrooms WebSocket-Handler /v1/responses akzeptierte die Browserverbindung, bevor er Origin im Handshake prüfte. Danach konnte der Proxy OPENAI_API_KEY aus seiner eigenen Umgebung zur Upstream-Anfrage hinzufügen. Die Same-Origin-Policy des Browsers verhindert nicht automatisch, dass JavaScript einen WebSocket zu einem anderen erreichbaren Host öffnet; der Browser sendet normalerweise Origin, und der Server muss Ursprünge außerhalb seiner Richtlinie ablehnen. Ein Angriff setzt daher voraus, dass der Browser eines Opfers einen verwundbaren Headroom-Proxy erreichen kann und der Proxy über nützliche Zugangsdaten oder Integrationen verfügt. Der Hinweis besagt, dass eine akzeptierte Verbindung beliebige LLM-Anfragen auslösen kann. Befehlsausführung auf dem Host hängt davon ab, ob die Anfrage ein konfiguriertes Werkzeug zur Befehlsausführung nutzen kann; sie folgt nicht automatisch aus der fehlenden Headerprüfung. Der CNA nennt Versionen vor 0.35.0 als betroffen, während die Paketmetadaten des GitHub-Hinweises 0.23.0 nennen. Aktualisieren Sie auf 0.35.0 oder neuer und prüfen Sie die installierte Version.

Schritt-für-Schritt Angriffsablauf

Schritt 1

Der Proxy besitzt Upstream-Berechtigungen

Ein Headroom-Prozess kann einen OpenAI-Schlüssel in OPENAI_API_KEY halten; der Browserclient muss den Schlüssel nicht selbst senden.

Schritt 2

Ein Browser öffnet eine nicht vertrauenswürdige Seite

Kann der Browser den Headroom-WebSocket-Endpunkt erreichen, kann das Seitenskript eine Verbindung versuchen. Der Browser setzt den Seitenursprung in den Handshake.

Schritt 3

Der verwundbare Handler akzeptiert ohne Ursprungspolitik

Im betroffenen Handler erreicht die Anfrage websocket.accept(), ohne vorher einen nicht vertrauenswürdigen Origin abzulehnen. Die Same-Origin-Policy ist keine serverseitige WebSocket-Autorisierung.

Schritt 4

Der Proxy leitet eine authentifizierte Anfrage weiter

Headroom kann einen fehlenden Upstream-Authorization-Header aus der Umgebung ergänzen. Die Angreiferseite kann dann Anfragen mit den Upstream-Rechten des Proxys auslösen; die Folgen hängen von Werkzeugen, Zugangsdaten, Netzwerkerreichbarkeit und Benutzerinteraktion ab.

Schritt 5

Die Korrektur lehnt unzulässige Ursprünge vor dem Routing ab

Der Upstream-Patch prüft den Ursprung vor Annahme und Weiterleitung der Sitzung. Browserursprünge sind standardmäßig auf Loopback begrenzt, erlaubte Ursprünge können konfiguriert werden, und unzulässige Verbindungen werden mit Code 1008 geschlossen.

Quellcode: Verwundbar vs. Sicher

Der Hinweis nennt handle_openai_responses_ws() und die fehlende Prüfung vor websocket.accept(). Commit c632023 fügt _is_allowed_websocket_origin() hinzu und lehnt unzulässige Ursprünge vor dem Routing ab. Native Clients ohne Origin bleiben kompatibel, Browserursprünge sind standardmäßig auf Loopback begrenzt und explizite Ursprünge lassen sich über HEADROOM_WS_ORIGINS oder HEADROOM_CORS_ORIGINS festlegen. Dieses kompakte Paar zeigt die geänderte Vertrauensgrenze; Hilfsfunktionen sind nicht vollständig enthalten. Upstream-Regressionstests decken fehlenden Origin, Loopback, entfernte und fehlerhafte Ursprünge sowie explizite Konfiguration ab.

Quelltextauszug: headroom/proxy/handlers/openai.py

VERWUNDBARE IMPLEMENTIERUNG
async def handle_openai_responses_ws(self, websocket: WebSocket) -> None:
    # Verwundbares Muster: Die Browsersitzung vor der Prüfung des Ursprungs akzeptieren.
    await websocket.accept()
    await self._relay_responses_session(websocket)
GEHÄRTETER SICHERHEITS-PATCH
async def handle_openai_responses_ws(self, websocket: WebSocket) -> None:
    ws_headers = dict(websocket.headers)
    if not _is_allowed_websocket_origin(ws_headers):
        logger.warning(
            "event=websocket_origin_not_allowed origin=%r",
            _header_get(ws_headers, "origin"),
        )
        await websocket.close(code=1008, reason="origin not allowed")
        return

    # Nur eine richtlinienkonforme Browsersitzung erreicht das Upstream-Routing.
    await websocket.accept()
    await self._relay_responses_session(websocket)

Checkliste für Engineering & Systemsicherheit

Quellen