From: Willy Tarreau Date: Tue, 3 Nov 2026 15:52:07 +0000 (+0100) Subject: MINOR: http/hardening: implement hardening "reject-header-trailer-folding" X-Git-Tag: v3.5-dev11~13 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=34200a02567a4cf975ed63aabe8f412696218e6f;p=haproxy.git MINOR: http/hardening: implement hardening "reject-header-trailer-folding" Rejects requests and responses which contain folded header fields or trailer fields. Folding consists in having a single field span multiple lines. This practice is obsoleted by modern HTTP standards, absolutely not seen anymore in valid messages over the internet but might still be present in a few antique internal services dealing with large cookies. On untrusted networks, they are always associated with attacks trying to inject headers into non-compliant components, and should be rejected as a sign of break-in attempt. Note that HAProxy does not have problems with them and systematically unfolds these messages before forwarding them. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index 73ab52582..7509e057a 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -34584,6 +34584,18 @@ http-hardening [from ] even if not rejected, duplicate host header fields are folded into a single one. + - reject-header-trailer-folding (inbound request and response) + Rejects requests and responses which contain folded header fields or + trailer fields. Folding consists in having a single field span multiple + lines. This practice is obsoleted by modern HTTP standards, absolutely + not seen anymore in valid messages over the internet but might still be + present in a few antique internal services dealing with large cookies. + On untrusted networks, they are always associated with attacks trying + to inject headers into non-compliant components, and should be rejected + as a sign of break-in attempt. Note that HAProxy does not have problems + with them and systematically unfolds these messages before forwarding + them. + - reject-lowercase-method (inbound request) Rejects requests whose method contains a lower-case character. No standard HTTP method uses lower-case characters and these ones are diff --git a/include/haproxy/http-t.h b/include/haproxy/http-t.h index 2637bec24..2504b9410 100644 --- a/include/haproxy/http-t.h +++ b/include/haproxy/http-t.h @@ -249,6 +249,7 @@ struct http_violation { #define HTTP_HRD_DEL_UNREG_HDR 0x00000040 #define HTTP_HRD_DEL_UNREG_TLR 0x00000080 #define HTTP_HRD_DEL_ALL_TLR 0x00000100 +#define HTTP_HRD_REJ_HDR_FOLD 0x00000200 /* insert new masks above this line */ #define HTTP_HRD_ALL 0xFFFFFFFF diff --git a/src/h1.c b/src/h1.c index 052e49e26..e3f55e3a0 100644 --- a/src/h1.c +++ b/src/h1.c @@ -914,6 +914,11 @@ int h1_headers_to_hdr_list(char *start, const char *stop, case H1_MSG_HDR_L1_LWS: http_msg_hdr_l1_lws: if (likely(HTTP_IS_SPHT(*ptr))) { + /* OBS-fold: either reject header folding or transform CR/LF to LWS */ + if (http_harden(HTTP_HRD_REJ_HDR_FOLD)) { + state = H1_MSG_HDR_L1_LWS; + goto http_msg_invalid; + } if (!skip_update) { /* replace HT,CR,LF with spaces */ for (; start + sov < ptr; sov++) @@ -988,6 +993,11 @@ int h1_headers_to_hdr_list(char *start, const char *stop, case H1_MSG_HDR_L2_LWS: http_msg_hdr_l2_lws: if (unlikely(HTTP_IS_SPHT(*ptr))) { + /* OBS-fold: either reject header folding or transform CR/LF to LWS */ + if (http_harden(HTTP_HRD_REJ_HDR_FOLD)) { + state = H1_MSG_HDR_L2_LWS; + goto http_msg_invalid; + } if (!skip_update) { /* LWS: replace HT,CR,LF with spaces */ for (; start + eol < ptr; eol++) diff --git a/src/http.c b/src/http.c index 4055b0528..647041c19 100644 --- a/src/http.c +++ b/src/http.c @@ -1784,6 +1784,7 @@ const struct http_hardening_kw http_hardening_kws[] = { { "delete-unregistered-header-fields",HTTP_HRD_DEL_UNREG_HDR, HTTP_DIR_BOTH }, { "delete-unregistered-trailer-fields",HTTP_HRD_DEL_UNREG_TLR, HTTP_DIR_BOTH }, { "delete-all-trailer-fields", HTTP_HRD_DEL_ALL_TLR, HTTP_DIR_BOTH }, + { "reject-header-trailer-folding", HTTP_HRD_REJ_HDR_FOLD, HTTP_DIR_BOTH }, /* insert new hardening options above this line */ { "all", HTTP_HRD_ALL, HTTP_DIR_BOTH }, { NULL, 0 }