FlawOpen

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

● CVE-2026-71416 · CVSS 8.8 · Tinggi
Riset Keamanan · FlawOpen

CVE-2026-71416: Pembajakan Origin WebSocket pada Headroom

Headroom tidak memeriksa Origin WebSocket; situs yang dibuka dapat meneruskan permintaan LLM melalui proksi. Perbarui ke 0.35.0+.

💡 Penjelasan Sederhana (ELI5)

Petugas resepsionis membiarkan siapa pun masuk ke loket privat karena tidak memeriksa kartu pengunjung. Origin adalah kartu itu: proksi harus memeriksanya sebelum membuka saluran yang dapat memakai kunci API tersimpan.

Konsep Kunci & Istilah

WebSocket
Koneksi dua arah yang bertahan lama, dimulai dengan permintaan HTTP Upgrade dan membawa pesan ke kedua arah.
Header Origin
Skema, host, dan port halaman yang dikirim browser untuk menunjukkan asal permintaan. Pada handshake WebSocket, server harus menerapkan kebijakan kepercayaannya sendiri; header CORS tidak menggantikan pemeriksaan ini.
CSWSH
Cross-Site WebSocket Hijacking: halaman tak tepercaya membuka WebSocket ke layanan yang dapat dijangkau, tetapi layanan tidak memverifikasi asal peminta.
Kredensial ambient
Rahasia yang sudah tersedia bagi proses, seperti kunci API dalam variabel lingkungan, yang dapat mengautentikasi permintaan tanpa kredensial dari klien.
CWE-1385
Origin tidak divalidasi pada WebSocket: endpoint tidak memeriksa apakah asal komunikasi diizinkan.

Pemetaan kerangka kerja

Analisis Akar Masalah (Root Cause)

Handler WebSocket /v1/responses milik Headroom menerima koneksi browser sebelum memeriksa Origin pada handshake. Setelah itu, proksi dapat menambahkan OPENAI_API_KEY dari lingkungannya sendiri ke permintaan upstream. Kebijakan same-origin browser tidak dengan sendirinya mencegah JavaScript memulai WebSocket ke host lain yang dapat dijangkau; browser biasanya mengirim Origin, dan server harus menolak asal di luar kebijakannya. Karena itu, eksploitasi mensyaratkan browser korban dapat menjangkau proksi Headroom yang rentan dan proksi memiliki kredensial atau integrasi yang berguna. Advisory menyatakan koneksi yang diterima dapat mengirim permintaan LLM arbitrer. Eksekusi perintah di host bergantung pada apakah permintaan dapat memakai alat yang dikonfigurasi untuk menjalankan perintah; hal itu bukan akibat otomatis dari pemeriksaan header yang hilang. CNA menyebut versi sebelum 0.35.0 terdampak, sedangkan metadata paket pada advisory GitHub menyebut 0.23.0. Perbarui ke 0.35.0 atau lebih baru dan verifikasi versi terpasang.

Alur Serangan Langkah demi Langkah

Langkah 1

Proksi memiliki kewenangan upstream

Proses Headroom dapat menyimpan kunci OpenAI dalam OPENAI_API_KEY; klien browser tidak perlu mengirim kunci tersebut.

Langkah 2

Browser membuka halaman tak tepercaya

Jika browser dapat menjangkau endpoint WebSocket Headroom, skrip halaman dapat mencoba terhubung. Browser menyertakan Origin halaman pada handshake.

Langkah 3

Handler rentan menerima tanpa kebijakan Origin

Pada handler terdampak, permintaan mencapai websocket.accept() tanpa lebih dahulu menolak Origin tak tepercaya. Kebijakan same-origin bukan pemeriksaan otorisasi WebSocket di server.

Langkah 4

Proksi meneruskan permintaan terautentikasi

Headroom dapat mengisi header Authorization upstream yang kosong dari lingkungan. Halaman yang dikendalikan penyerang lalu dapat memicu permintaan dengan kewenangan upstream proksi; dampaknya bergantung pada alat, kredensial, jangkauan jaringan, dan interaksi pengguna.

Langkah 5

Perbaikan menolak Origin terlarang sebelum routing

Patch upstream memeriksa Origin sebelum menerima dan meneruskan sesi. Origin browser secara default dibatasi ke loopback, Origin tertentu dapat dikonfigurasi, dan koneksi terlarang ditutup dengan kode 1008.

Kode Sumber: Rentan vs Aman

Advisory menunjuk handle_openai_responses_ws() dan pemeriksaan yang hilang sebelum websocket.accept(). Commit upstream c632023 menambahkan _is_allowed_websocket_origin() dan menolak Origin yang tidak diizinkan sebelum routing. Klien native tanpa Origin tetap didukung, Origin browser default-nya loopback, dan Origin eksplisit dapat diatur melalui HEADROOM_WS_ORIGINS atau HEADROOM_CORS_ORIGINS. Cuplikan ringkas ini menampilkan batas kepercayaan yang berubah; fungsi bantu tidak ditampilkan seluruhnya. Tes regresi upstream mencakup Origin kosong, loopback, remote, malformed, dan konfigurasi eksplisit.

Cuplikan sumber: headroom/proxy/handlers/openai.py

IMPLEMENTASI RENTAN
async def handle_openai_responses_ws(self, websocket: WebSocket) -> None:
    # Pola rentan: terima sesi browser sebelum memvalidasi origin.
    await websocket.accept()
    await self._relay_responses_session(websocket)
PERBAIKAN AMAN & KUAT
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

    # Hanya sesi browser yang disetujui kebijakan yang mencapai perutean upstream.
    await websocket.accept()
    await self._relay_responses_session(websocket)

Daftar Periksa Penguatan Sistem Rekayasa

Sumber