]> git.kaiwu.me - haproxy.git/commitdiff
MINOR: http/hardening: implement hardening "reject-header-trailer-folding"
authorWilly Tarreau <w@1wt.eu>
Tue, 3 Nov 2026 15:52:07 +0000 (16:52 +0100)
committerWilly Tarreau <w@1wt.eu>
Wed, 4 Nov 2026 21:45:33 +0000 (22:45 +0100)
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.

doc/configuration.txt
include/haproxy/http-t.h
src/h1.c
src/http.c

index 73ab52582b6829d8b11f638d4a0666307aacb0e7..7509e057a5dbe955bd2d8c51ea5ec89e07fc4252 100644 (file)
@@ -34584,6 +34584,18 @@ http-hardening <name> [from <name2>]
         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
index 2637bec248243bfd5befd45d0bb8feef317bb254..2504b94102e67bb91660cbf05d608b1f1cf6e129 100644 (file)
@@ -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
 
index 052e49e2609a582d3419e225f388d7d4f9e30480..e3f55e3a05f709a360ba3c233fd170cb903bf6eb 100644 (file)
--- 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++)
index 4055b0528064949250e46c250eefe4a6f4285753..647041c19c37cc9dc9ac2468b52fab540e471178 100644 (file)
@@ -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 }