Opened 5 weeks ago

Last modified 5 weeks 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:


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;

        return 200 ", host=$host, server_name=$server_name\n";
    server {
        listen 8012;

        return 200 " (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:',,

# As expected: dot-ended domains are unified
$ curl localhost:8012 -H 'Host:',,

# Unexpected: request with dot-ended domain will not be processed in usual virtual host
$ curl localhost:8012 -H 'Host:' (with dot at the end),,

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: .'
<head><title>400 Bad Request</title></head>
<center><h1>400 Bad Request</h1></center>

# 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;

        root "html/";
        return 401 "Unauthorized\n";

... and responses of the server running this config:

$ cat html/ 

# As expected: an access is unauthorized
$ curl localhost:8013/secret -H 'Host:'

# Unexpected: one may gain an unauthorized access
$ curl localhost:8013/ -H 'Host: .:.1234'

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, 5 weeks ago

There is one small and simple patch that fixes the issue and might satisfy code owners:

# HG changeset patch
# User Daniil Lemenkov <>
# 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
`` 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;
+            }
         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, now will be correct host header for, 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.