req_llm: WebSocket sessions do not verify TLS certificates, exposing API keys and OAuth tokens to on-path attackers
What fixes it
No source in this snapshot names a fixed version.
Description
Summary
ReqLLM versions 1.9.0 through 1.28.0 do not verify TLS certificates for secure WebSocket connections. Every affected wss:// connection accepts any certificate.
An attacker on the network path can terminate the connection with a certificate that the client does not trust. The attacker can then read the authorization: Bearer ... header and read or change the full WebSocket session.
Affected versions
- Affected:
>= 1.9.0, <= 1.28.0 - Fixed:
>= 1.29.0
The issue was introduced in v1.9.0 with OpenAI WebSocket streaming and Realtime support in PR #559.
Details
ReqLLM.Streaming.WebSocketSession.Client.start/3 in lib/req_llm/streaming/web_socket_session/client.ex called WebSockex.start/4 without :insecure, :cacerts, or :ssl_options.
WebSockex 0.5.1 defaults to insecure: true. This setting becomes :ssl.connect(..., verify: :verify_none). The ReqLLM option allowlist also did not pass TLS options, so callers could not enable certificate verification.
The affected paths are:
- OpenAI Responses WebSocket sessions.
- OpenAI Realtime sessions.
- OpenAI Codex WebSocket sessions.
These paths can send an OpenAI API key or an OAuth access token in the authorization header.
HTTP streaming through Finch and the HTTP/2 duplex session through Mint are not affected. These clients verify certificates by default.
Impact
An attacker must have an on-path position. Examples include a compromised network, hostile Wi-Fi, or DNS or BGP interference.
Successful exploitation can disclose:
- The API key or OAuth access token.
- Prompts, responses, tool calls, and tool results.
- Other data sent through the session.
The attacker can also change model output and tool calls before the application receives them.
Reproduction
- Start a TLS WebSocket endpoint with a self-signed certificate.
- Point an affected ReqLLM WebSocket session at that endpoint.
- Start the session with an
authorization: Bearer <token>header. - In an affected version, the TLS handshake succeeds and the endpoint receives the authorization header.
- A request to the same endpoint through an unaffected HTTP client fails because the certificate authority is not trusted.
Resolution
Upgrade to ReqLLM 1.29.0 or later.
Version 1.29.0:
- Enables peer verification for secure WebSocket connections.
- Uses the system certificate authorities by default.
- Verifies the certificate hostname.
- Fails closed if the system trust store is not available.
- Keeps support for an explicit custom certificate authority or custom SSL options.
- Applies the secure settings to
wss://URLs and WebSocket URLs built from anhttps://base URL. - Adds tests for an untrusted certificate, an incorrect hostname, and explicit custom trust settings.
The primary fix is in commit c7264fc0. The https:// base URL follow-up is in PR #1140 and commit edfe1108.
Workarounds
Upgrade to version 1.29.0 or later. If an upgrade is not possible, do not use the affected OpenAI Responses, Realtime, or Codex WebSocket paths. HTTP streaming is not affected.
Upstream note
The underlying WebSockex behavior is tracked in WebSockex issue #1. This advisory covers ReqLLM only.
Credit
Reported by Neil Berkman. Found with Claude Mythos.
Text from GitHub Advisory Database
Affected versions 1
- req_llm, erlang>= 1.9.0, <= 1.28.0GitHub