HTTP: Use common header validation function for HTTP/2 and HTTP/3

The header validation required by HTTP/2 and HTTP/3 is identical, so use
a common function for both.  This will make it easier to add additional
validation in the future.  Move the function to ngx_http_parse.c so that
it can share code with the HTTP/1.x parser.
This commit is contained in:
Demi Marie Obenour 2025-03-25 14:58:32 -04:00
parent 6bd9e8ce72
commit 3a45410074
3 changed files with 63 additions and 107 deletions

View File

@ -1093,6 +1093,66 @@ header_done:
}
#if (NGX_HTTP_V2 || NGX_HTTP_V3)
ngx_int_t
ngx_http_v23_validate_header(ngx_http_request_t *r, ngx_str_t *name,
ngx_str_t *value)
{
u_char ch;
ngx_uint_t i;
ngx_http_core_srv_conf_t *cscf;
r->invalid_header = 0;
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
if (name->len < 1) {
ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0,
"BUG: internal zero-length header name");
return NGX_ERROR;
}
for (i = (name->data[0] == ':'); i != name->len; i++) {
ch = name->data[i];
if ((ch >= 'a' && ch <= 'z')
|| (ch == '-')
|| (ch >= '0' && ch <= '9')
|| (ch == '_' && cscf->underscores_in_headers))
{
continue;
}
if (ch <= 0x20 || ch == 0x7f || ch == ':'
|| (ch >= 'A' && ch <= 'Z'))
{
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent invalid header name");
return NGX_ERROR;
}
r->invalid_header = 1;
}
for (i = 0; i != value->len; i++) {
ch = value->data[i];
if (ch == '\0' || ch == LF || ch == CR) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent header \"%V\" with "
"invalid value", name, value);
return NGX_ERROR;
}
}
return NGX_OK;
}
#endif
ngx_int_t
ngx_http_parse_uri(ngx_http_request_t *r)
{

View File

@ -142,8 +142,6 @@ static ngx_http_v2_out_frame_t *ngx_http_v2_get_frame(
static ngx_int_t ngx_http_v2_frame_handler(ngx_http_v2_connection_t *h2c,
ngx_http_v2_out_frame_t *frame);
static ngx_int_t ngx_http_v2_validate_header(ngx_http_request_t *r,
ngx_http_v2_header_t *header);
static ngx_int_t ngx_http_v2_pseudo_header(ngx_http_request_t *r,
ngx_http_v2_header_t *header);
static ngx_int_t ngx_http_v2_parse_path(ngx_http_request_t *r,
@ -1774,7 +1772,8 @@ ngx_http_v2_state_process_header(ngx_http_v2_connection_t *h2c, u_char *pos,
fc = r->connection;
/* TODO Optimization: validate headers while parsing. */
if (ngx_http_v2_validate_header(r, header) != NGX_OK) {
if (ngx_http_v23_validate_header(r, &header->name, &header->value)
!= NGX_OK) {
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
goto error;
}
@ -3232,56 +3231,6 @@ ngx_http_v2_get_closed_node(ngx_http_v2_connection_t *h2c)
}
static ngx_int_t
ngx_http_v2_validate_header(ngx_http_request_t *r, ngx_http_v2_header_t *header)
{
u_char ch;
ngx_uint_t i;
ngx_http_core_srv_conf_t *cscf;
r->invalid_header = 0;
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
for (i = (header->name.data[0] == ':'); i != header->name.len; i++) {
ch = header->name.data[i];
if ((ch >= 'a' && ch <= 'z')
|| (ch == '-')
|| (ch >= '0' && ch <= '9')
|| (ch == '_' && cscf->underscores_in_headers))
{
continue;
}
if (ch <= 0x20 || ch == 0x7f || ch == ':'
|| (ch >= 'A' && ch <= 'Z'))
{
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent invalid header name");
return NGX_ERROR;
}
r->invalid_header = 1;
}
for (i = 0; i != header->value.len; i++) {
ch = header->value.data[i];
if (ch == '\0' || ch == LF || ch == CR) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent header \"%V\" with "
"invalid value", &header->name);
return NGX_ERROR;
}
}
return NGX_OK;
}
static ngx_int_t
ngx_http_v2_pseudo_header(ngx_http_request_t *r, ngx_http_v2_header_t *header)
{

View File

@ -17,8 +17,6 @@ static void ngx_http_v3_cleanup_request(void *data);
static void ngx_http_v3_process_request(ngx_event_t *rev);
static ngx_int_t ngx_http_v3_process_header(ngx_http_request_t *r,
ngx_str_t *name, ngx_str_t *value);
static ngx_int_t ngx_http_v3_validate_header(ngx_http_request_t *r,
ngx_str_t *name, ngx_str_t *value);
static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r,
ngx_str_t *name, ngx_str_t *value);
static ngx_int_t ngx_http_v3_init_pseudo_headers(ngx_http_request_t *r);
@ -632,7 +630,7 @@ ngx_http_v3_process_header(ngx_http_request_t *r, ngx_str_t *name,
r->v3_parse->header_limit -= len;
if (ngx_http_v3_validate_header(r, name, value) != NGX_OK) {
if (ngx_http_v23_validate_header(r, name, value) != NGX_OK) {
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NGX_ERROR;
}
@ -692,57 +690,6 @@ ngx_http_v3_process_header(ngx_http_request_t *r, ngx_str_t *name,
}
static ngx_int_t
ngx_http_v3_validate_header(ngx_http_request_t *r, ngx_str_t *name,
ngx_str_t *value)
{
u_char ch;
ngx_uint_t i;
ngx_http_core_srv_conf_t *cscf;
r->invalid_header = 0;
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
for (i = (name->data[0] == ':'); i != name->len; i++) {
ch = name->data[i];
if ((ch >= 'a' && ch <= 'z')
|| (ch == '-')
|| (ch >= '0' && ch <= '9')
|| (ch == '_' && cscf->underscores_in_headers))
{
continue;
}
if (ch <= 0x20 || ch == 0x7f || ch == ':'
|| (ch >= 'A' && ch <= 'Z'))
{
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent invalid header name");
return NGX_ERROR;
}
r->invalid_header = 1;
}
for (i = 0; i != value->len; i++) {
ch = value->data[i];
if (ch == '\0' || ch == LF || ch == CR) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent header \"%V\" with "
"invalid value", name);
return NGX_ERROR;
}
}
return NGX_OK;
}
static ngx_int_t
ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name,
ngx_str_t *value)