summaryrefslogtreecommitdiffhomepage
path: root/src/http
diff options
context:
space:
mode:
authorSergey Kandaurov <pluknet@nginx.com>2025-11-04 16:34:32 +0400
committerRoman Arutyunyan <arutyunyan.roman@gmail.com>2025-11-26 19:51:40 +0400
commit511abb19e1e1b127f6d0943ccac346211a490a35 (patch)
tree0f48e61ffb621c0791ad67e2e8888441f21b64bd /src/http
parent6ed1188411882086e3518eda779ab782d8ab4d3f (diff)
downloadnginx-511abb19e1e1b127f6d0943ccac346211a490a35.tar.gz
nginx-511abb19e1e1b127f6d0943ccac346211a490a35.tar.bz2
Improved host header validation.
Validation is rewritten to follow RFC 3986 host syntax, based on ngx_http_parse_request_line(). The following is now rejected: - the rest of gen-delims "#", "?", "@", "[", "]" - other unwise delims <">, "<", ">", "\", "^", "`', "{", "|", "}" - IP literals with a trailing dot, missing closing bracket, or pct-encoded - a port subcomponent with invalid values - characters in upper half
Diffstat (limited to 'src/http')
-rw-r--r--src/http/ngx_http_request.c165
1 files changed, 133 insertions, 32 deletions
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
index 533af452f..557de6696 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -2184,72 +2184,173 @@ ngx_http_process_request(ngx_http_request_t *r)
ngx_int_t
ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc)
{
- u_char *h, ch;
- size_t i, dot_pos, host_len;
+ u_char *h, ch;
+ size_t i, dot_pos, host_len;
+ ngx_int_t port;
enum {
- sw_usual = 0,
- sw_literal,
- sw_rest
+ sw_host_start = 0,
+ sw_host,
+ sw_host_ip_literal,
+ sw_host_end,
+ sw_port,
} state;
dot_pos = host->len;
host_len = host->len;
+ port = 0;
h = host->data;
- state = sw_usual;
+ state = sw_host_start;
for (i = 0; i < host->len; i++) {
ch = h[i];
- switch (ch) {
+ switch (state) {
- case '.':
- if (dot_pos == i - 1) {
- return NGX_DECLINED;
- }
- dot_pos = i;
- break;
+ case sw_host_start:
- case ':':
- if (state == sw_usual) {
- host_len = i;
- state = sw_rest;
+ if (ch == '[') {
+ state = sw_host_ip_literal;
+ break;
}
- break;
- case '[':
- if (i == 0) {
- state = sw_literal;
- }
- break;
+ state = sw_host;
- case ']':
- if (state == sw_literal) {
- host_len = i + 1;
- state = sw_rest;
+ /* fall through */
+
+ case sw_host:
+
+ if (ch >= 'A' && ch <= 'Z') {
+ alloc = 1;
+ break;
}
- break;
- default:
+ if (ch >= 'a' && ch <= 'z') {
+ break;
+ }
- if (ngx_path_separator(ch)) {
- return NGX_DECLINED;
+ if (ch >= '0' && ch <= '9') {
+ break;
}
- if (ch <= 0x20 || ch == 0x7f) {
+ switch (ch) {
+ case ':':
+ host_len = i;
+ state = sw_port;
+ break;
+ case '-':
+ break;
+ case '.':
+ if (dot_pos == i - 1) {
+ return NGX_DECLINED;
+ }
+ dot_pos = i;
+ break;
+ case '_':
+ case '~':
+ /* unreserved */
+ break;
+ case '!':
+ case '$':
+ case '&':
+ case '\'':
+ case '(':
+ case ')':
+ case '*':
+ case '+':
+ case ',':
+ case ';':
+ case '=':
+ /* sub-delims */
+ break;
+ case '%':
+ /* pct-encoded */
+ break;
+ default:
return NGX_DECLINED;
}
+ break;
+
+ case sw_host_ip_literal:
if (ch >= 'A' && ch <= 'Z') {
alloc = 1;
+ break;
}
+ if (ch >= 'a' && ch <= 'z') {
+ break;
+ }
+
+ if (ch >= '0' && ch <= '9') {
+ break;
+ }
+
+ switch (ch) {
+ case ':':
+ break;
+ case ']':
+ host_len = i + 1;
+ state = sw_host_end;
+ break;
+ case '-':
+ break;
+ case '.':
+ if (dot_pos == i - 1) {
+ return NGX_DECLINED;
+ }
+ dot_pos = i;
+ break;
+ case '_':
+ case '~':
+ /* unreserved */
+ break;
+ case '!':
+ case '$':
+ case '&':
+ case '\'':
+ case '(':
+ case ')':
+ case '*':
+ case '+':
+ case ',':
+ case ';':
+ case '=':
+ /* sub-delims */
+ break;
+ default:
+ return NGX_DECLINED;
+ }
break;
+
+ case sw_host_end:
+
+ if (ch == ':') {
+ state = sw_port;
+ break;
+ }
+ return NGX_DECLINED;
+
+ case sw_port:
+
+ if (ch >= '0' && ch <= '9') {
+ if (port >= 6553 && (port > 6553 || (ch - '0') > 5)) {
+ return NGX_DECLINED;
+ }
+
+ port = port * 10 + (ch - '0');
+ break;
+ }
+ return NGX_DECLINED;
}
}
+ if (state == sw_host_ip_literal) {
+ return NGX_DECLINED;
+ }
+
if (dot_pos == host_len - 1) {
host_len--;
}