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

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

index e16c8b7dc7083dbd967297d9260ac328ebc22e38..ab698debb32726bdd68167cb90633bc90d31cb18 100644 (file)
@@ -34548,6 +34548,15 @@ http-hardening <name> [from <name2>]
         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
index 6a9adfd38b13842552ca37f48e9414db272062a7..efe0bd3401a01acb6b5e78ee54e3e8857de52b07 100644 (file)
@@ -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
 
index a1541eb8f5ecea895d7e96ff11465d93df2db94a..c6c9fccbfbfa6683d1142063a1a755b9d0b12610 100644 (file)
--- 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;
                                        }
index cf039af317d7318b9081dadf4331d6b5a91280bf..0ef5376674dce56b17e388c826269e0561ffa287 100644 (file)
@@ -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 }
index 2c1e5cd6f80456af6573aca36a2681202d354c92..89ceeb0906dc1530231c4a0b5ff7ddb31f092c53 100644 (file)
@@ -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