From: Willy Tarreau Date: Tue, 3 Nov 2026 15:12:54 +0000 (+0100) Subject: MINOR: http/hardening: implement hardening "reject-underscore-in-trailer-name" X-Git-Tag: v3.5-dev11~19 X-Git-Url: https://git.kaiwu.me/$%7BISSUES%7D/static/gitweb.js?a=commitdiff_plain;h=cc0e57965e01c0e9d3c7c9b801aa70cd4cbdfac3;p=haproxy.git MINOR: http/hardening: implement hardening "reject-underscore-in-trailer-name" This is the same as for the headers, but this time for the trailers. Better keep them separated as trailers are used even worse than headers these days. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index e16c8b7dc..ab698debb 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -34548,6 +34548,15 @@ http-hardening [from ] access-control policy for example. No standard HTTP header uses this, so it is safe to enable on untrusted listeners. + - reject-underscore-in-trailer-name (inbound, request and response) + Rejects requests and responses in which one tailer field name contains + an underscore character ('_' or \x5C). This character is perfectly + permitted, but some application frameworks which create automatic + variables tend to already turn the dash ('-') into an underscore in + variable names, leading to an ambiguity with the underscore which can + result in the same variable name. No standard HTTP trailer uses this, + so it is safe to enable on untrusted listeners. + /* * Local variables: * fill-column: 79 diff --git a/include/haproxy/http-t.h b/include/haproxy/http-t.h index 6a9adfd38..efe0bd340 100644 --- a/include/haproxy/http-t.h +++ b/include/haproxy/http-t.h @@ -243,6 +243,7 @@ struct http_violation { #define HTTP_HRD_REJ_DUP_HOST_HDR 0x00000001 #define HTTP_HRD_REJ_LCASE_METHOD 0x00000002 #define HTTP_HRD_REJ_USCORE_HDR_NAM 0x00000004 +#define HTTP_HRD_REJ_USCORE_TLR_NAM 0x00000008 /* insert new masks above this line */ #define HTTP_HRD_ALL 0xFFFFFFFF diff --git a/src/h1.c b/src/h1.c index a1541eb8f..c6c9fccbf 100644 --- a/src/h1.c +++ b/src/h1.c @@ -857,7 +857,8 @@ int h1_headers_to_hdr_list(char *start, const char *stop, if (isupper((unsigned char)*ptr) && h1m->flags & H1_MF_TOLOWER) *ptr = tolower((unsigned char)*ptr); else if (unlikely(*ptr == '_')) { - if ((h1m->state != H1_MSG_TRAILERS && http_harden(HTTP_HRD_REJ_USCORE_HDR_NAM))) { + if ((h1m->state != H1_MSG_TRAILERS && http_harden(HTTP_HRD_REJ_USCORE_HDR_NAM)) || + (h1m->state == H1_MSG_TRAILERS && http_harden(HTTP_HRD_REJ_USCORE_TLR_NAM))) { state = H1_MSG_RQMETH; goto http_msg_invalid; } diff --git a/src/http.c b/src/http.c index cf039af31..0ef537667 100644 --- a/src/http.c +++ b/src/http.c @@ -1778,6 +1778,7 @@ const struct http_hardening_kw http_hardening_kws[] = { { "reject-duplicate-host-header", HTTP_HRD_REJ_DUP_HOST_HDR, HTTP_DIR_REQ }, { "reject-lowercase-method", HTTP_HRD_REJ_LCASE_METHOD, HTTP_DIR_REQ }, { "reject-underscore-in-header-name", HTTP_HRD_REJ_USCORE_HDR_NAM, HTTP_DIR_BOTH }, + { "reject-underscore-in-trailer-name", HTTP_HRD_REJ_USCORE_TLR_NAM, HTTP_DIR_BOTH }, /* insert new hardening options above this line */ { "all", HTTP_HRD_ALL, HTTP_DIR_BOTH }, { NULL, 0 } diff --git a/src/http_htx.c b/src/http_htx.c index 2c1e5cd6f..89ceeb090 100644 --- a/src/http_htx.c +++ b/src/http_htx.c @@ -2030,6 +2030,12 @@ enum http_parser_status http_trailers_to_htx(struct http_hdr *list, struct htx * ret = HTTP_PRS_INV_HNAME; goto fail; } + + if (list[idx].n.ptr[i] == '_' && + http_harden(HTTP_HRD_REJ_USCORE_TLR_NAM)) { + ret = HTTP_PRS_INV_HNAME; + goto fail; + } } /* All field names affecting decoding or routing are forbidden