diff --git a/configure.ac b/configure.ac index 03ffadd89b27..413cda2aebc7 100644 --- a/configure.ac +++ b/configure.ac @@ -1622,6 +1622,7 @@ AC_CHECK_LIB([htp], [htp_config_set_compression_bomb_limit],AC_DEFINE_UNQUOTED([HAVE_HTP_CONFIG_SET_COMPRESSION_BOMB_LIMIT],[1],[Found htp_config_set_compression_bomb_limit function in libhtp]) ,,[-lhtp]) AC_CHECK_LIB([htp], [htp_config_set_compression_time_limit],AC_DEFINE_UNQUOTED([HAVE_HTP_CONFIG_SET_COMPRESSION_TIME_LIMIT],[1],[Found htp_config_set_compression_time_limit function in libhtp]) ,,[-lhtp]) AC_CHECK_LIB([htp], [htp_config_set_max_tx],AC_DEFINE_UNQUOTED([HAVE_HTP_CONFIG_SET_MAX_TX],[1],[Found htp_config_set_max_tx function in libhtp]) ,,[-lhtp]) + AC_CHECK_LIB([htp], [htp_config_set_number_headers_limit],AC_DEFINE_UNQUOTED([HAVE_HTP_CONFIG_SET_HEADERS_LIMIT],[1],[Found htp_config_set_number_headers_limit function in libhtp]) ,,[-lhtp]) ]) if test "x$enable_non_bundled_htp" = "xno"; then @@ -1648,6 +1649,7 @@ AC_DEFINE_UNQUOTED([HAVE_HTP_CONFIG_SET_COMPRESSION_BOMB_LIMIT],[1],[Assuming htp_config_set_compression_bomb_limit function in bundled libhtp]) AC_DEFINE_UNQUOTED([HAVE_HTP_CONFIG_SET_COMPRESSION_TIME_LIMIT],[1],[Assuming htp_config_set_compression_time_limit function in bundled libhtp]) AC_DEFINE_UNQUOTED([HAVE_HTP_CONFIG_SET_MAX_TX],[1],[Assuming htp_config_set_max_tx function in bundled libhtp]) + AC_DEFINE_UNQUOTED([HAVE_HTP_CONFIG_SET_HEADERS_LIMIT],[1],[Assuming htp_config_set_number_headers_limit function in bundled libhtp]) else echo echo " ERROR: Libhtp is not bundled. Get libhtp by doing:" diff --git a/doc/userguide/configuration/suricata-yaml.rst b/doc/userguide/configuration/suricata-yaml.rst index 24233b2bb79e..3dfc950ec641 100644 --- a/doc/userguide/configuration/suricata-yaml.rst +++ b/doc/userguide/configuration/suricata-yaml.rst @@ -1588,6 +1588,10 @@ use of libhtp. #compression-bomb-limit: 1 Mb # Maximum time spent decompressing a single transaction in usec #decompression-time-limit: 100000 + # Maximum number of live transactions per flow + #max-tx: 512 + # Maximum used number of HTTP1 headers in one request or response + #headers-limit: 1024 Other parameters are customizable from Suricata. :: diff --git a/rules/http-events.rules b/rules/http-events.rules index b5cce76bf235..e08d84eb46cd 100644 --- a/rules/http-events.rules +++ b/rules/http-events.rules @@ -93,4 +93,7 @@ alert http any any -> any any (msg:"SURICATA HTTP failed protocol change"; flow: alert http any any -> any any (msg:"SURICATA HTTP request missing protocol"; flow:established,to_server; app-layer-event:http.request_line_missing_protocol; classtype:protocol-command-decode; sid:2221055; rev:1;) -# next sid 2221056 +alert http any any -> any any (msg:"SURICATA HTTP request too many headers"; flow:established,to_server; app-layer-event:http.request_too_many_headers; classtype:protocol-command-decode; sid:2221056; rev:1;) +alert http any any -> any any (msg:"SURICATA HTTP response too many headers"; flow:established,to_client; app-layer-event:http.response_too_many_headers; classtype:protocol-command-decode; sid:2221057; rev:1;) + +# next sid 2221058 diff --git a/src/app-layer-htp.c b/src/app-layer-htp.c index e327696ab027..3f3037ca30a1 100644 --- a/src/app-layer-htp.c +++ b/src/app-layer-htp.c @@ -170,6 +170,9 @@ SCEnumCharMap http_decoder_event_table[] = { { "REQUEST_CHUNK_EXTENSION", HTTP_DECODER_EVENT_REQUEST_CHUNK_EXTENSION }, { "REQUEST_LINE_MISSING_PROTOCOL", HTTP_DECODER_EVENT_REQUEST_LINE_MISSING_PROTOCOL }, + { "REQUEST_TOO_MANY_HEADERS", HTTP_DECODER_EVENT_REQUEST_TOO_MANY_HEADERS }, + { "RESPONSE_TOO_MANY_HEADERS", HTTP_DECODER_EVENT_RESPONSE_TOO_MANY_HEADERS }, + /* suricata warnings/errors */ { "MULTIPART_GENERIC_ERROR", HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR }, { "MULTIPART_NO_FILEDATA", HTTP_DECODER_EVENT_MULTIPART_NO_FILEDATA }, @@ -629,6 +632,8 @@ struct { HTTP_DECODER_EVENT_DUPLICATE_CONTENT_LENGTH_FIELD_IN_RESPONSE }, { "Request chunk extension", HTTP_DECODER_EVENT_REQUEST_CHUNK_EXTENSION }, { "Request line: missing protocol", HTTP_DECODER_EVENT_REQUEST_LINE_MISSING_PROTOCOL }, + { "Too many request headers", HTTP_DECODER_EVENT_REQUEST_TOO_MANY_HEADERS }, + { "Too many response headers", HTTP_DECODER_EVENT_RESPONSE_TOO_MANY_HEADERS }, }; #define HTP_ERROR_MAX (sizeof(htp_errors) / sizeof(htp_errors[0])) @@ -2101,6 +2106,10 @@ static void HTPConfigSetDefaultsPhase1(HTPCfgRec *cfg_prec) #ifdef HAVE_HTP_CONFIG_SET_MAX_TX #define HTP_CONFIG_DEFAULT_MAX_TX_LIMIT 512 htp_config_set_max_tx(cfg_prec->cfg, HTP_CONFIG_DEFAULT_MAX_TX_LIMIT); +#endif +#ifdef HAVE_HTP_CONFIG_SET_HEADERS_LIMIT +#define HTP_CONFIG_DEFAULT_HEADERS_LIMIT 1024 + htp_config_set_number_headers_limit(cfg_prec->cfg, HTP_CONFIG_DEFAULT_HEADERS_LIMIT); #endif /* libhtp <= 0.5.9 doesn't use soft limit, but it's impossible to set * only the hard limit. So we set both here to the (current) htp defaults. @@ -2461,6 +2470,17 @@ static void HTPConfigParseParameters(HTPCfgRec *cfg_prec, ConfNode *s, /* set default soft-limit with our new hard limit */ SCLogConfig("Setting HTTP max-tx limit to %" PRIu32 " bytes", limit); htp_config_set_max_tx(cfg_prec->cfg, limit); +#endif +#ifdef HAVE_HTP_CONFIG_SET_HEADERS_LIMIT + } else if (strcasecmp("headers-limit", p->name) == 0) { + uint32_t limit = 0; + if (ParseSizeStringU32(p->val, &limit) < 0) { + FatalError("failed to parse 'headers-limit' " + "from conf file - %s.", + p->val); + } + SCLogConfig("Setting HTTP headers limit to %" PRIu32, limit); + htp_config_set_number_headers_limit(cfg_prec->cfg, limit); #endif } else if (strcasecmp("randomize-inspection-sizes", p->name) == 0) { if (!g_disable_randomness) { diff --git a/src/app-layer-htp.h b/src/app-layer-htp.h index 9c39ba393ffd..55ff9c9b9fce 100644 --- a/src/app-layer-htp.h +++ b/src/app-layer-htp.h @@ -126,6 +126,8 @@ enum { HTTP_DECODER_EVENT_RANGE_INVALID, HTTP_DECODER_EVENT_REQUEST_CHUNK_EXTENSION, HTTP_DECODER_EVENT_REQUEST_LINE_MISSING_PROTOCOL, + HTTP_DECODER_EVENT_REQUEST_TOO_MANY_HEADERS, + HTTP_DECODER_EVENT_RESPONSE_TOO_MANY_HEADERS, /* suricata errors/warnings */ HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR, diff --git a/suricata.yaml.in b/suricata.yaml.in index 3e73623c4b21..013411ece4e3 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -1098,6 +1098,8 @@ app-layer: #decompression-time-limit: 100000 # Maximum number of live transactions per flow #max-tx: 512 + # Maximum used number of HTTP1 headers in one request or response + #headers-limit: 1024 server-config: