]> git.kaiwu.me - haproxy.git/commitdiff
MINOR: http: define http_hdng_chk for ambient hardening checks to be run
authorWilly Tarreau <w@1wt.eu>
Thu, 29 Oct 2026 14:14:45 +0000 (15:14 +0100)
committerWilly Tarreau <w@1wt.eu>
Wed, 4 Nov 2026 21:45:32 +0000 (22:45 +0100)
In order to enforce HTTP hardening checks, we have to instrument functions
at many different layers of the HTTP parsing code. First tests show that
we might need to add extra parameters to numerous functions, just to
forward requirements and results.

A simpler, less elegant but much more efficient approach consists sharing
the parsing checks globally (local to the thread) so that anywhere in the
HTTP stack it is possible to know what checks are enforced. It is not
different from the global acceptable violations after all, except that
this has to be preset, consumed and cleared by the caller. The benefit
is that it will then be possible to define the checks before parsing,
and let any function anywhere in the chain participate to the operation,
including for reporting possible anomalies.

The http_harden(flag) function does the repetitive part of the check and
returning what final action was performed and should be easy to deploy.

include/haproxy/http-t.h
include/haproxy/http.h
src/http.c

index 684a69325e14a0f21307e4fd003aaa3fdaba3218..7635231fb23b86bb81ba16b74c771e621fb1d7a8 100644 (file)
@@ -270,6 +270,18 @@ struct http_hdng_prf {
        char *file;                 /* file of the declaration */
 };
 
+/* description of a check to be done in the HTTP stack:
+ * - rules = mask of the checks to apply (comes from the profile) ;
+ * - found = mask of checks met even if not blocked, preset to zero
+ *   before the call and filled by the stack ;
+ * - fixed = mask of actions performed
+ */
+struct http_hdng_chk {
+       uint32_t rules; // rules to apply
+       uint32_t found; // what was found
+       uint32_t fixed; // mask of actions performed
+};
+
 #endif /* _HAPROXY_HTTP_T_H */
 
 /*
index d862db6ae58d7333b71e98d70d9d062fd8652fef..33b73df0001553cfcc859cdb794bd6facbc865d5 100644 (file)
@@ -39,6 +39,8 @@ extern const struct http_hardening_kw http_hardening_kws[];
 extern struct http_hdng_prf http_hardening_untrusted;
 extern struct http_hdng_prf http_hardening_trusted;
 
+extern THREAD_LOCAL struct http_hdng_chk http_hdng_chk;
+
 enum http_meth_t find_http_meth(const char *str, const int len);
 int http_get_status_idx(unsigned int status);
 const char *http_get_reason(unsigned int status);
@@ -104,6 +106,34 @@ static inline int http_is_param_delimiter(char c, char delim)
        return c == '&' || c == ';' || c == delim;
 }
 
+/* reset an http hardening check after a call */
+static inline void http_reset_hardening_check(struct http_hdng_chk *chk)
+{
+       chk->rules = 0;
+       chk->found = 0;
+       chk->fixed = 0;
+}
+
+/* marks the global http_hdng_chk.found with the provided check, then checks if this
+ * test is enabled or not. If so, it will put it in the hardening fixed value
+ * and return true, otherwise it will return false. This allows to construct
+ * simple checks like this and have all fields properly filled:
+ *
+ *     if (unlikely(smiley_in_header_name))
+ *         if (http_harden(HTTP_HRD_SMILEY_HNAME))
+ *             goto fail;
+ *
+ * Make sure to pass it single-bit options to keep found/fixed easy to grasp.
+ */
+static inline int http_harden(uint32_t flag)
+{
+       http_hdng_chk.found |= flag;
+       if (!(http_hdng_chk.rules & flag))
+               return 0;
+       http_hdng_chk.fixed |= flag;
+       return 1;
+}
+
 /* Match language range with language tag. RFC2616 14.4:
  *
  *    A language-range matches a language-tag if it exactly equals
index daec94fe35cbb72a27a4c3306ef5d6d4ef1b1ca9..c3d2502bae1a4e306aa59b3a7668b134a35b0475 100644 (file)
@@ -377,6 +377,11 @@ const struct ist http_known_methods[HTTP_METH_OTHER] = {
 long http_err_status_codes[512 / sizeof(long)] = { };
 long http_fail_status_codes[512 / sizeof(long)] = { };
 
+/* hardening checks to be run through the HTTP stack, preset by the caller,
+ * must always be cleared by the one that sets or checks them after use.
+ */
+THREAD_LOCAL struct http_hdng_chk http_hdng_chk = { };
+
 /*
  * returns a known method among HTTP_METH_* or HTTP_METH_OTHER for all unknown
  * ones.