flawopen.com/Teardowns/cve-2026-71416-headroom-websocket-origin-hijacking
CVE-2026-71416: secuestro de Origin WebSocket en Headroom
Headroom no validaba Origin en WebSocket; un sitio visitado podía enviar solicitudes LLM por el proxy accesible. Actualice a 0.35.0+.
Un recepcionista deja entrar a cualquiera a un mostrador privado porque no comprueba la credencial del visitante. Origin es esa credencial: el proxy debe validarla antes de abrir un canal que pueda usar su clave API almacenada.
Conceptos Clave y Términos
WebSocket- Conexión persistente y bidireccional que comienza con una solicitud HTTP de actualización y transporta mensajes en ambos sentidos.
Cabecera Origin- Esquema, host y puerto que el navegador envía para identificar la página que inició una solicitud. En el handshake WebSocket, el servidor debe aplicar su propia política de confianza; CORS no sustituye esta comprobación.
CSWSH- Cross-Site WebSocket Hijacking: una página no confiable abre un WebSocket hacia un servicio accesible que no valida el origen iniciador.
Credencial ambiental- Secreto disponible para el proceso, como una clave API en una variable de entorno, que puede autenticar solicitudes sin una credencial propia del cliente.
CWE-1385- Falta de validación de Origin en WebSockets: el endpoint no comprueba si el origen de comunicación está permitido.
Mapeos de marcos
- Referencia CWE para la debilidad descrita en esta guía: CWE-1385
- OWASP GenAI 2025 — LLM06:2025 Agencia excesiva
Análisis de Causa Raíz
El handler WebSocket /v1/responses de Headroom aceptaba la conexión del navegador antes de comprobar Origin en el handshake. Después, el proxy podía añadir OPENAI_API_KEY de su propio entorno a la solicitud upstream. La política de mismo origen del navegador no impide por sí sola que JavaScript inicie un WebSocket a otro host accesible; el navegador suele enviar Origin, y el servidor debe rechazar los orígenes que no cumplan su política. Por tanto, la explotación requiere que el navegador de la víctima alcance un proxy Headroom vulnerable y que este tenga credenciales o integraciones útiles. El aviso indica que la conexión aceptada puede emitir solicitudes LLM arbitrarias. La ejecución de comandos en el host depende de que la solicitud pueda usar una herramienta configurada para ejecutarlos; no es una consecuencia automática de no validar la cabecera. El CNA indica que están afectadas las versiones anteriores a 0.35.0, mientras que los metadatos del aviso GitHub nombran 0.23.0. Actualice a 0.35.0 o posterior y verifique la versión instalada.
Flujo de Ataque Paso a Paso
El proxy conserva autoridad upstream
Un proceso Headroom puede tener una clave OpenAI en OPENAI_API_KEY; el cliente del navegador no necesita enviar esa clave.
El navegador visita una página no confiable
Si puede alcanzar el endpoint WebSocket de Headroom, el script de la página puede intentar conectarse. El navegador incluye el origen de la página en el handshake.
El handler vulnerable acepta sin política de origen
En el handler afectado, la solicitud llega a websocket.accept() sin rechazar antes un Origin no confiable. La política de mismo origen no es una autorización WebSocket del servidor.
El proxy reenvía una solicitud autenticada
Headroom puede completar una cabecera Authorization upstream ausente con el valor del entorno. La página del atacante puede provocar solicitudes con la autoridad upstream del proxy; el impacto depende de herramientas, credenciales, alcance de red e interacción del usuario.
La corrección rechaza orígenes no permitidos antes de enrutar
El parche upstream comprueba el origen antes de aceptar y reenviar la sesión. Por defecto limita los orígenes del navegador a loopback, admite orígenes configurados y cierra una conexión no permitida con el código 1008.
Código Fuente: Vulnerable vs. Seguro
El aviso identifica handle_openai_responses_ws() y la comprobación ausente antes de websocket.accept(). El commit c632023 añade _is_allowed_websocket_origin() y rechaza un origen no permitido antes del enrutamiento; conserva clientes nativos sin Origin, usa loopback como valor predeterminado para navegadores y admite orígenes configurados mediante HEADROOM_WS_ORIGINS o HEADROOM_CORS_ORIGINS. Este par breve aísla el límite de confianza modificado; no reproduce todos los auxiliares. Las pruebas upstream cubren Origin ausente, loopback, origen remoto, formato incorrecto y configuración explícita.
Fragmento de origen: headroom/proxy/handlers/openai.py
async def handle_openai_responses_ws(self, websocket: WebSocket) -> None:
# Patrón vulnerable: acepta la sesión del navegador antes de validar el origen.
await websocket.accept()
await self._relay_responses_session(websocket)
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
# Solo una sesión del navegador aprobada por la política llega al enrutamiento upstream.
await websocket.accept()
await self._relay_responses_session(websocket)
Lista de Verificación de Seguridad para Ingeniería
- ✓Actualice Headroom a 0.35.0 o posterior e inventarie la versión instalada y la imagen desplegada, no solo el archivo de dependencias.
- ✓Valide
Origindel WebSocket antes de llamar aaccept()o reenviar mensajes; deniegue orígenes no locales salvo que estén permitidos explícitamente. - ✓Mantenga la compatibilidad con clientes nativos sin
Originsolo si su tipo se verifica por separado; la ausencia de origen en un navegador no autoriza. - ✓No use
*en la lista de orígenes permitidos de un proxy con credenciales; enumere los orígenes de confianza y pruebe la normalización de puertos predeterminados. - ✓Cuando sea posible, vincule el proxy local de desarrollo a loopback y exija autenticación o controles de red si otros hosts pueden alcanzarlo.
- ✓Use claves upstream limitadas y de corta duración cuando sea posible; no exponga shell ni herramientas de alto impacto a solicitudes que crucen el límite del navegador.
- ✓Añada pruebas para confirmar que un Origin remoto se cierra con una violación de política antes del enrutamiento upstream, mientras se mantienen los casos previstos de loopback, lista permitida y cliente nativo aprobado.
- ✓Trate la ejecución de comandos como dependiente de las herramientas configuradas; el fallo de Origin no equivale a RCE automática del host.