Patch-Source: https://github.com/nginx/nginx/commit/365694160a85229a7cb006738de9260d49ff5fa2 -- From 2f71e4a6fb3f6f7c34b61657dbbea39b0aa507ce Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Fri, 24 May 2024 00:20:01 +0300 Subject: [PATCH] Added max_headers directive. The directive limits the number of request headers accepted from clients. While the total amount of headers is believed to be sufficiently limited by the existing buffer size limits (client_header_buffer_size and large_client_header_buffers), the additional limit on the number of headers might be beneficial to better protect backend servers. Requested by Maksim Yevmenkin. Signed-off-by: Elijah Zupancic Origin: --- src/http/ngx_http_core_module.c | 10 ++++++++++ src/http/ngx_http_core_module.h | 2 ++ src/http/ngx_http_request.c | 9 +++++++++ src/http/ngx_http_request.h | 1 + src/http/v2/ngx_http_v2.c | 9 +++++++++ src/http/v3/ngx_http_v3_request.c | 9 +++++++++ 6 files changed, 40 insertions(+) diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c index 033a3bf64..4d11eb323 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -252,6 +252,13 @@ static ngx_command_t ngx_http_core_commands[] = { offsetof(ngx_http_core_srv_conf_t, large_client_header_buffers), NULL }, + { ngx_string("max_headers"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_core_srv_conf_t, max_headers), + NULL }, + { ngx_string("ignore_invalid_headers"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, @@ -3460,6 +3467,7 @@ ngx_http_core_create_srv_conf(ngx_conf_t *cf) cscf->request_pool_size = NGX_CONF_UNSET_SIZE; cscf->client_header_timeout = NGX_CONF_UNSET_MSEC; cscf->client_header_buffer_size = NGX_CONF_UNSET_SIZE; + cscf->max_headers = NGX_CONF_UNSET_UINT; cscf->ignore_invalid_headers = NGX_CONF_UNSET; cscf->merge_slashes = NGX_CONF_UNSET; cscf->underscores_in_headers = NGX_CONF_UNSET; @@ -3501,6 +3509,8 @@ ngx_http_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) return NGX_CONF_ERROR; } + ngx_conf_merge_uint_value(conf->max_headers, prev->max_headers, 1000); + ngx_conf_merge_value(conf->ignore_invalid_headers, prev->ignore_invalid_headers, 1); diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h index 765e7ff60..5af748e1c 100644 --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -198,6 +198,8 @@ typedef struct { ngx_msec_t client_header_timeout; + ngx_uint_t max_headers; + ngx_flag_t ignore_invalid_headers; ngx_flag_t merge_slashes; ngx_flag_t underscores_in_headers; diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 9593b7fb5..97ed5a3c8 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -1489,6 +1489,15 @@ ngx_http_process_request_headers(ngx_event_t *rev) /* a header line has been parsed successfully */ + if (r->headers_in.count++ >= cscf->max_headers) { + r->lingering_close = 1; + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent too many header lines"); + ngx_http_finalize_request(r, + NGX_HTTP_REQUEST_HEADER_TOO_LARGE); + break; + } + h = ngx_list_push(&r->headers_in.headers); if (h == NULL) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index 65c8333f8..22452802a 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -182,6 +182,7 @@ typedef struct { typedef struct { ngx_list_t headers; + ngx_uint_t count; ngx_table_elt_t *host; ngx_table_elt_t *connection; diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index 0f5bd3de8..a4cce4f77 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -1817,6 +1817,15 @@ ngx_http_v2_state_process_header(ngx_http_v2_connection_t *h2c, u_char *pos, } } else { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + if (r->headers_in.count++ >= cscf->max_headers) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent too many header lines"); + ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE); + goto error; + } + h = ngx_list_push(&r->headers_in.headers); if (h == NULL) { return ngx_http_v2_connection_error(h2c, diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 0faddd23b..75fecbbfb 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -665,6 +665,15 @@ ngx_http_v3_process_header(ngx_http_request_t *r, ngx_str_t *name, } } else { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + if (r->headers_in.count++ >= cscf->max_headers) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent too many header lines"); + ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE); + return NGX_ERROR; + } + h = ngx_list_push(&r->headers_in.headers); if (h == NULL) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); -- 2.54.0