Where
-Infinity
0

Errata: the subject of the original message is bogus and should instead read,

Host ambiguous requests through NGINX $httphost and Debian's proxyparams

Sorry for that,

-- Gabriel Corona

an attacker could exploit this ambiguity to bypass security restrictions configured in NGINX (of have other security impacts).

Example:

GET http://foo/ HTTP/1.1 <- Used for virtual host routing     User-Agent: UA     Host: bar                <- Used for $httphost

Debian's proxyparam file used to use $httphost:

proxysetheader Host $httphost;

This has been fixed as part of Debian bug #1126960 [1].

In this case, "foo" is used for virtual host routing but the request is forwarded as:

GET / HTTP/1.1     User-Agent: UA     Host: bar     X-Real-IP: ...     X-Forwarded-For: ...     X-Forwarded-Proto: ...

If the backend application actually makes use of the Host value, this might have a security impact. For example,

if the backend application uses the Host value for tenant dispatching; logging, etc),

then this might have a security impact.

Example NGINX configuration:

server {       listen 443 ssl;       servername tenant1;       sslcertificate           /etc/nginx/ssl/tenant1.crt;       sslcertificatekey       /etc/nginx/ssl/tenant1.key;       location / {         proxypass http://backend;         include proxyparams;       }   }   server {       listen 443 ssl;       servername host2;       sslcertificate           /etc/nginx/ssl/tenant2.crt;       sslcertificatekey       /etc/nginx/ssl/tenant2.key;       location / {         allow 10.0.0.0/8;         allow 192.168.0.0/16;         allow 127.0.0.1/8;         deny all;         proxypass http://backend;         include proxyparams;       }   }

In this example, tenant2 is expected to be only available from private IP addresses. However, an attacker could target tenant2 with:

GET http://tenant1/ HTTP/1.1 <- Used for virtual host routing     User-Agent: UA     Host: tenant2                <- Used for $httphost

Another potential application, would be to send access logs of an attack to the log files of the wrong tenant.

You might be impacted if:

NGINX is directly exposed; you use $httphost (eg. through Debian's proxyparams).

NGINX's position ----------------

NGINX's position is that the bug is to use $http host. NGINX's documentation recommends using $host [2]:

proxysetheader Host $host;

I would claim that NGINX could (should?):

1. either reject host ambiguous requests altogether   (because they are attacks right?); 2. or override the HTTP header with the value from the request line.

These behaviors are consistent with what other (open-soruce) HTTP server do:

1. NGINX when using HTTP/2, HA proxy for solution 1; 2. Traefik, Caddy, Apache HTTPD for solution 2.

At the very least, the security impact of using $host vs $httphost should be better documented.

Mitigations -----------

Mitigation 1: always use $host instead of $httphost:

proxysetheader Host             $host;     proxysetheader X-Forwarded-Host $host;

Mitigation 1b: always use $servername instead of $httphost:

proxysetheader Host             $servername;     proxysetheader X-Forwarded-Host $servername; proxysetheader Host             "www.example.com";     proxysetheader X-Forwarded-Host "www.example.com"; Does not work when using ports:     if ($host != $httphost) {         return 421 "Ambiguous host";     }

# Since nginx 1.29.3:     if ($httphost != "$host$isrequestport$requestport") {         return 421 "Ambiguous host";     }

Debian info -----------

Snippet from Debian changelogs:

nginx (1.26.3-3+deb13u4) trixie; urgency=medium            d/conf/params: use "$host" instead of "$httphost" requests with a conflicting Host header) backend applications (uwsgi, fastcgi, scgi, proxy)         switch to "$host" as a safer, normalized alternative         note: this changes behaviour, as "$host" does not preserve the a port number may be affected

New proxyparams file:

# !!! Security workaround !!!     # Do not set the Host header as "$httphost".     #     # "$httphost" is the Host header exactly as supplied by the client. with a different Host header, for example:     #     #     GET https://example.com/ HTTP/1.1     #     Host: malformedhost     # behaviour.     # "$requestport", allowing Host to be constructed as:     #     $host$isrequestport$requestport     # It avoids forwarding an untrusted raw Host header to the backend.     # may therefore break or behave differently after this change.          proxysetheader Host $host;     proxysetheader X-Real-IP $remoteaddr;     proxysetheader X-Forwarded-For $proxyaddxforwardedfor;     proxysetheader X-Forwarded-Proto $scheme;

[1] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1126960 [2] https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/ Regard,

-- Gabriel Corona

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203