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 <e.zupancic@f5.com>
Origin: <199dc0d6b0>
This commit is contained in:
Maxim Dounin 2024-05-24 00:20:01 +03:00 committed by Sergey Kandaurov
parent 06c30ec29d
commit 365694160a
6 changed files with 40 additions and 0 deletions

View file

@ -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,
@ -3511,6 +3518,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;
@ -3552,6 +3560,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);

View file

@ -199,6 +199,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;

View file

@ -1494,6 +1494,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);

View file

@ -184,6 +184,7 @@ typedef struct {
typedef struct {
ngx_list_t headers;
ngx_uint_t count;
ngx_table_elt_t *host;
ngx_table_elt_t *connection;

View file

@ -1823,6 +1823,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,

View file

@ -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);