HTTP: reject invalid header names

HTTP headers must be an RFC9110 token, so only a subset of characters
are permitted.  RFC9113 and RFC9114 require rejecting invalid header
characters in HTTP/2 and HTTP/3 respectively, so reject them in HTTP/1.0
and HTTP/1.1 for consistency.  This also requires removing the ignore
hack for (presumably ancient) versions of IIS.
This commit is contained in:
Demi Marie Obenour 2025-03-14 01:00:59 -04:00
parent bccf2b1f3b
commit 2fbc33e2de

View File

@ -811,6 +811,29 @@ done:
return NGX_OK;
}
static ngx_int_t
ngx_http_non_alnum_dash_header_char(u_char ch)
{
switch (ch) {
case '!':
case '#':
case '$':
case '%':
case '&':
case '\'':
case '*':
case '+':
case '.':
case '^':
case '_':
case '`':
case '|':
case '~':
return 1;
default:
return 0;
}
}
ngx_int_t
ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b,
@ -824,7 +847,6 @@ ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b,
sw_space_before_value,
sw_value,
sw_space_after_value,
sw_ignore_line,
sw_almost_done,
sw_header_almost_done
} state;
@ -875,22 +897,14 @@ ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b,
break;
}
if (ch == '_') {
if (allow_underscores) {
hash = ngx_hash(0, ch);
r->lowcase_header[0] = ch;
i = 1;
} else {
hash = 0;
i = 0;
r->invalid_header = 1;
}
if (ch == '_' && allow_underscores) {
hash = ngx_hash(0, ch);
r->lowcase_header[0] = ch;
i = 1;
break;
}
if (ch <= 0x20 || ch == 0x7f || ch == ':') {
if (!ngx_http_non_alnum_dash_header_char(ch)) {
r->header_end = p;
return NGX_HTTP_PARSE_INVALID_HEADER;
}
@ -949,17 +963,7 @@ ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b,
goto done;
}
/* IIS may send the duplicate "HTTP/1.1 ..." lines */
if (ch == '/'
&& r->upstream
&& p - r->header_name_start == 4
&& ngx_strncmp(r->header_name_start, "HTTP", 4) == 0)
{
state = sw_ignore_line;
break;
}
if (ch <= 0x20 || ch == 0x7f) {
if (!ngx_http_non_alnum_dash_header_char(ch)) {
r->header_end = p;
return NGX_HTTP_PARSE_INVALID_HEADER;
}
@ -1034,17 +1038,6 @@ ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b,
}
break;
/* ignore header line */
case sw_ignore_line:
switch (ch) {
case LF:
state = sw_start;
break;
default:
break;
}
break;
/* end of header line */
case sw_almost_done:
switch (ch) {
@ -1125,8 +1118,7 @@ ngx_http_v23_validate_header(ngx_http_request_t *r, ngx_str_t *name,
continue;
}
if (ch <= 0x20 || ch == 0x7f || ch == ':'
|| (ch >= 'A' && ch <= 'Z'))
if (!ngx_http_non_alnum_dash_header_char(ch))
{
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent invalid header name");