Opened 17 months ago
Last modified 11 months ago
#2650 new defect
Uncovered edge case in host header validation
| Reported by: | Daniil Lemenkov | Owned by: | |
|---|---|---|---|
| Priority: | minor | Milestone: | |
| Component: | nginx-core | Version: | 1.25.x |
| Keywords: | host, patch | Cc: | |
| uname -a: | Linux Blue 5.19.0-46-generic #47-Ubuntu SMP PREEMPT_DYNAMIC Fri Jun 16 13:30:11 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux | ||
| nginx -V: |
nginx version: nginx/1.27.0
built by gcc 12.2.0 (Ubuntu 12.2.0-3ubuntu1) configure arguments: |
||
Description
Hello to maintainers, developers and anybody interested!
I suppose there is an uncovered edge case during host header validation procedure. It is likely to be caused by a code in ngx_http_validate_host.
Consider the following nginx.conf file:
events {}
http {
server {
listen 8012 default_server;
server_name _;
return 200 "default_server, host=$host, server_name=$server_name\n";
}
server {
listen 8012;
server_name example.com;
return 200 "example.com, host=$host, server_name=$server_name\n";
}
server {
listen 8012;
server_name example.com.;
return 200 "example.com. (with dot at the end), host=$host, server_name=$server_name\n";
}
}
... and responses of the server running the config:
$ curl localhost:8012 -H 'Host: example.com' example.com, host=example.com, server_name=example.com # As expected: dot-ended domains are unified $ curl localhost:8012 -H 'Host: example.com.' example.com, host=example.com, server_name=example.com # Unexpected: request with dot-ended domain will not be processed in usual virtual host $ curl localhost:8012 -H 'Host: example.com.:1234.' example.com. (with dot at the end), host=example.com., server_name=example.com.
There are unexpected header validation results in cases when a port part of the host header contains a dot.
Although the last request is probably wrong, I suppose Nginx should provide consistent behavior in the case too to allow users to rely on it.
You may evolve these requests to a more strange one (within the same server with the same config):
# As expected: '.' is incorrect host $ curl localhost:8012 -H 'Host: .' <html> <head><title>400 Bad Request</title></head> <body> <center><h1>400 Bad Request</h1></center> <hr><center>nginx/1.27.0</center> </body> </html> # Unexpected: the server handle and process a bad request successfully $ curl localhost:8012 -H 'Host: .:12.34' default_server, host=., server_name=_
In this case a request with a single-dot host header, which is forbidden usually, succeeds now.
This may have some negative impact on configurations with an authorization based on use of $host variable. Consider another nginx.conf file:
events {}
http {
server {
listen 8013 default_server;
server_name _;
root "html/$host";
}
server {
listen 8013;
server_name secret.example.com;
root "html/secret.example.com";
return 401 "Unauthorized\n";
}
}
... and responses of the server running this config:
$ cat html/secret.example.com/secret SomeSecret # As expected: an access is unauthorized $ curl localhost:8013/secret -H 'Host: secret.example.com' Unauthorized # Unexpected: one may gain an unauthorized access $ curl localhost:8013/secret.example.com/secret -H 'Host: .:.1234' SomeSecret
Using this configuration looks like a bad approach for an authorization, nevertheless, I think the issue may cause some other unexpected cases in other applications.
I wish you consider the issue important enough to be acknowledged. To increase your interest for fixing it, I will try to prepare a patch. Hope this helps.
Thank you all a lot!

There is one small and simple patch that fixes the issue and might satisfy code owners:
# HG changeset patch # User Daniil Lemenkov <nginx@lemenkov.com> # Date 1718146557 -10800 # Wed Jun 12 01:55:57 2024 +0300 # Node ID dc2b4168599c001e3b2e33ad8462b0e3973bc54b # Parent 02e9411009b987f408214ab4a8b6b6093f843bcd HTTP: Ignore dots in port in 'ngx_http_validate_host' Fix `ngx_http_validate_host` function. Do not process dots after `]` and `:` literals in any special way. This prevents misbehaviour at validating host headers with dots after a colon (in a port part) like `example.com.:80.00` or even `.:.`. diff -r 02e9411009b9 -r dc2b4168599c src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Tue May 28 17:22:30 2024 +0400 +++ b/src/http/ngx_http_request.c Wed Jun 12 01:55:57 2024 +0300 @@ -2169,7 +2169,9 @@ if (dot_pos == i - 1) { return NGX_DECLINED; } - dot_pos = i; + if (state != sw_rest) { + dot_pos = i; + } break; case ':':Roughly speaking, it disables special dot processing after
:or](insw_reststate).Pros: small, simple.
Cons: disables some extra checks for rest part of a host header (for instance,
example.com.:1234..now will be correct host header forexample.com), and therefore breaks two tests in nginx-tests.I also see another option to fix the issue — to rewrite a bit the whole
ngx_http_validate_host— however I don't know your preferences on such a big patches, so I tried to start with the simplest one.