mirror of https://github.com/nginx/nginx
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
This commit is contained in:
parent
6ed1188411
commit
511abb19e1
|
|
@ -2186,68 +2186,169 @@ ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc)
|
||||||
{
|
{
|
||||||
u_char *h, ch;
|
u_char *h, ch;
|
||||||
size_t i, dot_pos, host_len;
|
size_t i, dot_pos, host_len;
|
||||||
|
ngx_int_t port;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
sw_usual = 0,
|
sw_host_start = 0,
|
||||||
sw_literal,
|
sw_host,
|
||||||
sw_rest
|
sw_host_ip_literal,
|
||||||
|
sw_host_end,
|
||||||
|
sw_port,
|
||||||
} state;
|
} state;
|
||||||
|
|
||||||
dot_pos = host->len;
|
dot_pos = host->len;
|
||||||
host_len = host->len;
|
host_len = host->len;
|
||||||
|
port = 0;
|
||||||
|
|
||||||
h = host->data;
|
h = host->data;
|
||||||
|
|
||||||
state = sw_usual;
|
state = sw_host_start;
|
||||||
|
|
||||||
for (i = 0; i < host->len; i++) {
|
for (i = 0; i < host->len; i++) {
|
||||||
ch = h[i];
|
ch = h[i];
|
||||||
|
|
||||||
switch (ch) {
|
switch (state) {
|
||||||
|
|
||||||
|
case sw_host_start:
|
||||||
|
|
||||||
|
if (ch == '[') {
|
||||||
|
state = sw_host_ip_literal;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
state = sw_host;
|
||||||
|
|
||||||
|
/* fall through */
|
||||||
|
|
||||||
|
case sw_host:
|
||||||
|
|
||||||
|
if (ch >= 'A' && ch <= 'Z') {
|
||||||
|
alloc = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch >= 'a' && ch <= 'z') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch >= '0' && ch <= '9') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ch) {
|
||||||
|
case ':':
|
||||||
|
host_len = i;
|
||||||
|
state = sw_port;
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
break;
|
||||||
case '.':
|
case '.':
|
||||||
if (dot_pos == i - 1) {
|
if (dot_pos == i - 1) {
|
||||||
return NGX_DECLINED;
|
return NGX_DECLINED;
|
||||||
}
|
}
|
||||||
dot_pos = i;
|
dot_pos = i;
|
||||||
break;
|
break;
|
||||||
|
case '_':
|
||||||
case ':':
|
case '~':
|
||||||
if (state == sw_usual) {
|
/* unreserved */
|
||||||
host_len = i;
|
|
||||||
state = sw_rest;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
case '!':
|
||||||
case '[':
|
case '$':
|
||||||
if (i == 0) {
|
case '&':
|
||||||
state = sw_literal;
|
case '\'':
|
||||||
}
|
case '(':
|
||||||
|
case ')':
|
||||||
|
case '*':
|
||||||
|
case '+':
|
||||||
|
case ',':
|
||||||
|
case ';':
|
||||||
|
case '=':
|
||||||
|
/* sub-delims */
|
||||||
break;
|
break;
|
||||||
|
case '%':
|
||||||
case ']':
|
/* pct-encoded */
|
||||||
if (state == sw_literal) {
|
|
||||||
host_len = i + 1;
|
|
||||||
state = sw_rest;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
||||||
if (ngx_path_separator(ch)) {
|
|
||||||
return NGX_DECLINED;
|
return NGX_DECLINED;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
if (ch <= 0x20 || ch == 0x7f) {
|
case sw_host_ip_literal:
|
||||||
return NGX_DECLINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ch >= 'A' && ch <= 'Z') {
|
if (ch >= 'A' && ch <= 'Z') {
|
||||||
alloc = 1;
|
alloc = 1;
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
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) {
|
if (dot_pos == host_len - 1) {
|
||||||
|
|
|
||||||
|
|
@ -476,68 +476,169 @@ ngx_stream_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc)
|
||||||
{
|
{
|
||||||
u_char *h, ch;
|
u_char *h, ch;
|
||||||
size_t i, dot_pos, host_len;
|
size_t i, dot_pos, host_len;
|
||||||
|
ngx_int_t port;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
sw_usual = 0,
|
sw_host_start = 0,
|
||||||
sw_literal,
|
sw_host,
|
||||||
sw_rest
|
sw_host_ip_literal,
|
||||||
|
sw_host_end,
|
||||||
|
sw_port,
|
||||||
} state;
|
} state;
|
||||||
|
|
||||||
dot_pos = host->len;
|
dot_pos = host->len;
|
||||||
host_len = host->len;
|
host_len = host->len;
|
||||||
|
port = 0;
|
||||||
|
|
||||||
h = host->data;
|
h = host->data;
|
||||||
|
|
||||||
state = sw_usual;
|
state = sw_host_start;
|
||||||
|
|
||||||
for (i = 0; i < host->len; i++) {
|
for (i = 0; i < host->len; i++) {
|
||||||
ch = h[i];
|
ch = h[i];
|
||||||
|
|
||||||
switch (ch) {
|
switch (state) {
|
||||||
|
|
||||||
|
case sw_host_start:
|
||||||
|
|
||||||
|
if (ch == '[') {
|
||||||
|
state = sw_host_ip_literal;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
state = sw_host;
|
||||||
|
|
||||||
|
/* fall through */
|
||||||
|
|
||||||
|
case sw_host:
|
||||||
|
|
||||||
|
if (ch >= 'A' && ch <= 'Z') {
|
||||||
|
alloc = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch >= 'a' && ch <= 'z') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch >= '0' && ch <= '9') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ch) {
|
||||||
|
case ':':
|
||||||
|
host_len = i;
|
||||||
|
state = sw_port;
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
break;
|
||||||
case '.':
|
case '.':
|
||||||
if (dot_pos == i - 1) {
|
if (dot_pos == i - 1) {
|
||||||
return NGX_DECLINED;
|
return NGX_DECLINED;
|
||||||
}
|
}
|
||||||
dot_pos = i;
|
dot_pos = i;
|
||||||
break;
|
break;
|
||||||
|
case '_':
|
||||||
case ':':
|
case '~':
|
||||||
if (state == sw_usual) {
|
/* unreserved */
|
||||||
host_len = i;
|
|
||||||
state = sw_rest;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
case '!':
|
||||||
case '[':
|
case '$':
|
||||||
if (i == 0) {
|
case '&':
|
||||||
state = sw_literal;
|
case '\'':
|
||||||
}
|
case '(':
|
||||||
|
case ')':
|
||||||
|
case '*':
|
||||||
|
case '+':
|
||||||
|
case ',':
|
||||||
|
case ';':
|
||||||
|
case '=':
|
||||||
|
/* sub-delims */
|
||||||
break;
|
break;
|
||||||
|
case '%':
|
||||||
case ']':
|
/* pct-encoded */
|
||||||
if (state == sw_literal) {
|
|
||||||
host_len = i + 1;
|
|
||||||
state = sw_rest;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
||||||
if (ngx_path_separator(ch)) {
|
|
||||||
return NGX_DECLINED;
|
return NGX_DECLINED;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
if (ch <= 0x20 || ch == 0x7f) {
|
case sw_host_ip_literal:
|
||||||
return NGX_DECLINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ch >= 'A' && ch <= 'Z') {
|
if (ch >= 'A' && ch <= 'Z') {
|
||||||
alloc = 1;
|
alloc = 1;
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
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) {
|
if (dot_pos == host_len - 1) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue