Opened 6 months ago

Last modified 9 days 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!

Change History (1)

comment:1 by Daniil Lemenkov, 6 months ago

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 ] (in sw_rest state).

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 for example.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.

Note: See TracTickets for help on using tickets.