even if not rejected, duplicate host header fields are folded into a
single one.
+ - 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
+ essentially used in desynchronization attack scenarios, by trying to
+ exploit different case matching schemes along the chain. This is
+ recommended on untrusted listeners.
+
/*
* Local variables:
* fill-column: 79
#define HTTP_HRD_NONE 0x00000000
/* insert new masks below this line */
#define HTTP_HRD_REJ_DUP_HOST_HDR 0x00000001
+#define HTTP_HRD_REJ_LCASE_METHOD 0x00000002
/* insert new masks above this line */
#define HTTP_HRD_ALL 0xFFFFFFFF
return 0;
}
-/* Check that method only contains token as required.
- * See RFC 9110 9. Methods
+/* Check that method only contains token as required. See RFC 9110 9. Methods.
+ * Can also enforce non-lower case if hardening requires it.
*/
static inline int http_method_has_forbidden_char(const struct ist ist)
{
do {
if (!HTTP_IS_TOKEN(*start))
return 1;
+
+ if (unlikely(islower((unsigned char)*start))) {
+ if (http_harden(HTTP_HRD_REJ_LCASE_METHOD))
+ return 1;
+ }
start++;
} while (start < istend(ist));
return 0;
case H1_MSG_RQMETH:
http_msg_rqmeth:
- if (likely(HTTP_IS_TOKEN(*ptr)))
+ if (likely(HTTP_IS_TOKEN(*ptr))) {
+ if (unlikely(islower((unsigned char)*ptr))) {
+ if (http_harden(HTTP_HRD_REJ_LCASE_METHOD)) {
+ state = H1_MSG_RQMETH;
+ goto http_msg_invalid;
+ }
+ }
EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rqmeth, http_msg_ood, state, H1_MSG_RQMETH);
+ }
if (likely(HTTP_IS_SPHT(*ptr))) {
sl.rq.m.len = ptr - sl.rq.m.ptr;
}
/* The method is a non-empty token (RFC7231#4.1) */
- if (!meth_sl.len)
- goto fail;
- for (i = 0; i < meth_sl.len; i++) {
- if (!HTTP_IS_TOKEN(meth_sl.ptr[i]))
- htx->flags |= HTX_FL_PARSING_ERROR;
- }
+ if (!istlen(meth_sl) || http_method_has_forbidden_char(meth_sl))
+ htx->flags |= HTX_FL_PARSING_ERROR;
/* make sure the final URI isn't empty. Note that 7540#8.1.2.3 states
* that :path must not be empty.
const struct http_hardening_kw http_hardening_kws[] = {
/* insert new hardening options below this line */
{ "reject-duplicate-host-header", HTTP_HRD_REJ_DUP_HOST_HDR, HTTP_DIR_REQ },
+ { "reject-lowercase-method", HTTP_HRD_REJ_LCASE_METHOD, HTTP_DIR_REQ },
/* insert new hardening options above this line */
{ "all", HTTP_HRD_ALL, HTTP_DIR_BOTH },
{ NULL, 0 }