Skip to content

connectd: treat websocket handshake headers as case-insensitive#9308

Open
coreyphillips wants to merge 1 commit into
ElementsProject:masterfrom
coreyphillips:connectd-ws-case-insensitive-headers
Open

connectd: treat websocket handshake headers as case-insensitive#9308
coreyphillips wants to merge 1 commit into
ElementsProject:masterfrom
coreyphillips:connectd-ws-case-insensitive-headers

Conversation

@coreyphillips

Copy link
Copy Markdown

Fixes #9307.

The websocket handshake parser matches header names byte-exactly (get_http_hdr) and the Upgrade/Connection values with case-sensitive strstr. Header field names are case-insensitive per RFC 9110 section 5.1, and RFC 6455 says the Upgrade and Connection values are "treated as an ASCII case-insensitive value" (already quoted in the comment above http_respond).

Practical impact: Node.js's built-in WebSocket (undici) lowercases all request headers, so it can never connect to a bind-addr=ws: listener and gets "400 I only speak websocket". Browsers happen to send RFC casing and work.

Changes:

  • get_http_hdr matches header names case-insensitively (strncasecmp, as used elsewhere in the tree)
  • Upgrade/Connection values use a small case-insensitive contains helper
  • Sec-WebSocket-Version stays an exact "13" comparison and the key is base64 data, both unchanged
  • run-websocket.c gains a second handshake with lowercased header names and mixed-case values, as sent by Node; same Sec-WebSocket-Accept expected

Verified the failure mode against a live v26.06.1 bind-addr=ws: listener with raw replays (lowercased names rejected before, accepted with this change per the unit test vector).

HTTP header field names are case-insensitive (RFC 9110 section 5.1) and
RFC 6455 says the Upgrade and Connection values are treated as ASCII
case-insensitive, as the comment in websocketd.c already quotes. The
handshake parser matched header names byte-exactly and the values with
case-sensitive strstr, so any client that normalizes header casing got
400: notably Node.js's built-in WebSocket (undici) lowercases all
request headers and can never connect to a bind-addr=ws: listener,
while browsers happen to send RFC casing and work.

Match header names case-insensitively in get_http_hdr and use a
case-insensitive search for the Upgrade and Connection values. The
Sec-WebSocket-Version value comparison stays exact and the key is
base64 data, unchanged. Extends the unit test with a lowercased
handshake as sent by Node.

Fixes ElementsProject#9307

Changelog-Fixed: connectd: the WebSocket port now accepts handshakes from clients that send lowercase HTTP header names, such as Node.js's built-in WebSocket.
Signed-off-by: Corey Phillips <corey.lyle.phillips@proton.me>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

connectd websocket: HTTP handshake headers matched case-sensitively, rejecting RFC-compliant clients

1 participant