MINOR: h1: permit to relax the websocket checks for missing mandatory headers

At least one user would like to allow a standards-violating client setup
WebSocket connections through haproxy to a standards-violating server that
accepts them. While this should of course never be done over the internet,
it can make sense in the datacenter between application components which do
not need to mask the data, so this typically falls into the situation of
what the "accept-unsafe-violations-in-http-request" option and the
"accept-unsafe-violations-in-http-response" option are made for.
See GH #2876 for more context.

This patch relaxes the test on the "Sec-Websocket-Key" header field in
the request, and of the "Sec-Websocket-Accept" header in the response
when these respective options are set.

The doc was updated to reference this addition. This may be backported
to 3.1 but preferably not further.
This commit is contained in:
Willy Tarreau 2025-02-28 13:35:13 +01:00
parent 0e08252294
commit fd5d59967a
2 changed files with 24 additions and 9 deletions

View File

@ -9444,6 +9444,9 @@ no option accept-unsafe-violations-in-http-request
different protocol names (e.g. RTSP), and multiple digits for both the
major and the minor version.
* In H1 only, WebSocket (RFC6455) requests failing to present a valid
"Sec-Websocket-Key" header field will be accepted.
This option should never be enabled by default as it hides application bugs
and open security breaches. It should only be deployed after a problem has
been confirmed.
@ -9494,6 +9497,9 @@ no option accept-unsafe-violations-in-http-response
different protocol names (e.g. RTSP), and multiple digits for both the
major and the minor version.
* In H1 only, WebSocket (RFC6455) responses failing to present a valid
"Sec-Websocket-Accept" header field will be accepted.
This option should never be enabled by default as it hides application bugs
and open security breaches. It should only be deployed after a problem has
been confirmed.

View File

@ -1850,7 +1850,7 @@ static void h1_set_tunnel_mode(struct h1s *h1s)
* responsible for the generation of a key. This happens when a h2 client is
* interfaced with a h1 server.
*
* Returns 0 if no key found or invalid key
* Returns 0 if no key found or invalid key. The message is not modified.
*/
static int h1_search_websocket_key(struct h1s *h1s, struct h1m *h1m, struct htx *htx)
{
@ -1900,11 +1900,9 @@ static int h1_search_websocket_key(struct h1s *h1s, struct h1m *h1m, struct htx
}
}
/* missing websocket key, reject the message */
if (!ws_key_found) {
htx->flags |= HTX_FL_PARSING_ERROR;
/* missing websocket key, invalid message */
if (!ws_key_found)
return 0;
}
return 1;
}
@ -1986,13 +1984,24 @@ static size_t h1_handle_headers(struct h1s *h1s, struct h1m *h1m, struct htx *ht
if ((h1m->flags & (H1_MF_CONN_UPG|H1_MF_UPG_WEBSOCKET)) ==
(H1_MF_CONN_UPG|H1_MF_UPG_WEBSOCKET)) {
int ws_ret = h1_search_websocket_key(h1s, h1m, htx);
if (!ws_ret) {
h1s->flags |= H1S_F_PARSING_ERROR;
TRACE_ERROR("missing/invalid websocket key, reject H1 message", H1_EV_RX_DATA|H1_EV_RX_HDRS|H1_EV_H1S_ERR, h1s->h1c->conn, h1s);
h1_capture_bad_message(h1s->h1c, h1s, h1m, buf);
ret = 0;
goto end;
if ((!(h1m->flags & H1_MF_RESP) && !(h1s->h1c->px->options2 & PR_O2_REQBUG_OK)) ||
((h1m->flags & H1_MF_RESP) && !(h1s->h1c->px->options2 & PR_O2_RSPBUG_OK))) {
htx->flags |= HTX_FL_PARSING_ERROR;
h1s->flags |= H1S_F_PARSING_ERROR;
TRACE_ERROR("missing/invalid websocket key, reject H1 message",
H1_EV_RX_DATA|H1_EV_RX_HDRS|H1_EV_H1S_ERR, h1s->h1c->conn, h1s);
ret = 0;
goto end;
} else {
TRACE_ERROR("missing/invalid websocket key, but accepting this "
"violation according to configuration",
H1_EV_RX_DATA|H1_EV_RX_HDRS|H1_EV_H1S_ERR, h1s->h1c->conn, h1s);
}
}
}