<div dir="ltr"><div dir="ltr"><br></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">Le ven. 27 févr. 2026 à 09:55, Gabriel Corona <<a href="mailto:gabriel.corona@free.fr">gabriel.corona@free.fr</a>> a écrit :<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">In addition to the issue discussed by OP, using $http_host could<br>
possibly introduce vulnerabilities to HTTP Host ambiguity attacks.<br>
<br>
Debian's /etc/nginx/proxy_param uses the following line:<br>
<br>
proxy_set_header Host $http_host;<br>
<br>
This configuration is not consistent which what is recommended by<br>
NGINX documentation [1]:<br>
<br>
proxy_set_header Host $host;<br>
<br>
Note that the nginx-snippets package contains a<br>
common-proxy-pass-headers.conf file which includes a correct<br>
configuration:<br>
<br>
proxy_set_header Host $host;<br>
proxy_set_header X-Forwarded-Host $host;<br>
<br>
An attacker can use a HTTP/1 request with ambiguous hosts:<br>
<br>
GET <a href="http://host1/" rel="noreferrer" target="_blank">http://host1/</a> HTTP/1.1<br>
User-Agent: UA<br>
Host: host2<br>
<br>
This type of request is accepted by NGINX when using HTTP/1 only.<br>
<br>
In this case, with "proxy_set_header Host $http_host;":<br>
<br>
* server name "host1" is used for virtual host dispatching in NGINX;<br>
* server name "host2" is passed to upstream HTTP server.<br>
<br>
If NGINX is used to apply security restrictions/filtering to a<br>
multi-host/multi-tenant backend application, this could be used to apply<br>
the NGINX rules of "host1" while targeting upstream "host2.<br>
<br>
For example:<br>
<br>
* if NGINX is applying some IP adresse restriction to host2,<br>
the attacker can target host2 without these restrictions [a];<br>
* if host1 and host2 are using different mTLS configuration,<br>
the attackers could use their mTLS keypair for host1 and<br>
target host2;<br>
* if the upstream server uses the host/authority (probably a bad<br>
idea) to build absolute URLs, an attacker could use<br>
host-ambiguous requests to generate links to malicious servers<br>
(especially with cache poisoning).<br>
<br>
I reached to NGINX to discuss about this potential host ambiguity and<br>
I suggested using a "safer" logic such as:<br>
<br>
* overriding the Host header with the request line authority if present<br>
(same as Apacher httpd, Traefik and Caddy);<br>
* rejecting host-ambiguous requests entirely (same as NGINX in HTTP/2,<br>
HA Proxy).<br>
<br>
Their assessment is that this is a configuration error i.e. you<br>
should use $host and not $http_host.<br>
<br>
More generally, other usages of $http_host introduce a host ambiguity<br>
issue which might have a security impact (eg. for in proxy_pass,<br>
access_log, etc.).<br>
<br>
[a]:<br>
<br>
server {<br>
listen 443 ssl;<br>
server_name host1;<br>
ssl_certificate /etc/nginx/ssl/host1.crt;<br>
ssl_certificate_key /etc/nginx/ssl/host1.key;<br>
location / {<br>
proxy_pass <a href="http://backend" rel="noreferrer" target="_blank">http://backend</a>;<br>
include proxy_params;<br>
}<br>
}<br>
server {<br>
listen 443 ssl;<br>
server_name host2;<br>
ssl_certificate /etc/nginx/ssl/host2.crt;<br>
ssl_certificate_key /etc/nginx/ssl/host2.key;<br>
location / {<br>
allow <a href="http://10.0.0.0/8" rel="noreferrer" target="_blank">10.0.0.0/8</a>;<br>
allow <a href="http://192.168.0.0/16" rel="noreferrer" target="_blank">192.168.0.0/16</a>;<br>
allow <a href="http://127.0.0.1/8" rel="noreferrer" target="_blank">127.0.0.1/8</a>;<br>
deny all;<br>
proxy_pass <a href="http://backend" rel="noreferrer" target="_blank">http://backend</a>;<br>
include proxy_params;<br>
}<br>
}<br>
<br>
[b]:<br>
<br>
server {<br>
listen 443 ssl;<br>
server_name host1;<br>
ssl_certificate /etc/nginx/ssl/host1.crt;<br>
ssl_certificate_key /etc/nginx/ssl/host1.key;<br>
ssl_client_certificate /etc/nginx/ssl/ca-host1.crt;<br>
ssl_verify_client on;<br>
ssl_verify_depth 2;<br>
location / {<br>
proxy_pass <a href="http://backend" rel="noreferrer" target="_blank">http://backend</a>;<br>
include proxy_params;<br>
}<br>
}<br>
server {<br>
listen 443 ssl;<br>
server_name host2;<br>
ssl_certificate /etc/nginx/ssl/host2.crt;<br>
ssl_certificate_key /etc/nginx/ssl/host2.key;<br>
ssl_client_certificate /etc/nginx/ssl/ca-host1.crt;<br>
ssl_verify_client on;<br>
ssl_verify_depth 2;<br>
location / {<br>
proxy_pass <a href="http://backend" rel="noreferrer" target="_blank">http://backend</a>;<br>
include proxy_params;<br>
}<br>
}<br>
<br>
=== Potential fixes/mitigation<br>
<br>
Mitigation 0: use the hardcoded expected hostname instead of `$http_host`.<br>
<br>
proxy_set_header Host "<a href="http://www.example.com" rel="noreferrer" target="_blank">www.example.com</a>";<br>
proxy_set_header X-Forwarded-Host "<a href="http://www.example.com" rel="noreferrer" target="_blank">www.example.com</a>";<br>
<br>
Downside: not suitable for NGINX proxy_params.<br>
<br>
Mitigation 1: use $host instead of $http_host:<br>
<br>
proxy_set_header Host $host;<br>
proxy_set_header X-Forwarded-Host $host;<br>
<br>
Downside: this not not include the port information which could<br>
be a breaking change for some applications.</blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
Mitigation 1: use $host$is_request_port$request_port instead of $http_host:<br>
<br>
proxy_set_header Host $host$is_request_port$request_port;<br>
proxy_set_header X-Forwarded-Host $host$is_request_port$request_port;<br>
<br>
Downside: only available since NGINX 1.29.3 (not in trixie). </blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
Note: I think the $request_port could be spoofed anyway. </blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
Mitigation 2: use NGINX directive to reject ambiguous (malicious) requests.<br>
<br>
Either (does not work when using ports):<br>
<br>
if ($host != $http_host) {<br>
return 421 "Ambiguous host";<br>
}<br>
<br>
or:<br>
<br>
if ($http_host != "$host$is_request_port$request_port") {<br>
return 421 "Ambiguous host";<br>
}<br>
<br>
=== Q: Should NGINX accept or rejects HTTP requests with host ambiguity?<br>
<br>
Host-ambiguous requests are accepted by NGINX based on RFC 9112 section <br>
3.2.2 [2]:<br>
<br>
> When an origin server receives a request with an absolute-form of<br>
> request-target, the origin server MUST ignore the received Host<br>
> header field (if any) and instead use the host information of<br>
> the request-target. Note that if the request-target does not<br>
> have an authority component, an empty Host header field<br>
> will be sent in this case.<br>
<br>
NGINX is acting as a reverse proxy and is therefore [3] considered<br>
an origin server in this context.<br>
<br>
On the other hand, I would argue that rejecting the request would make<br>
sense as per section 3.2 [4]:<br>
<br>
> A client MUST send a Host header field (Section 7.2 of [HTTP])<br>
> in all HTTP/1.1 request messages. If the target URI includes an<br>
> authority component, then a client MUST send a field value for Host<br>
> that is identical to that authority component, excluding any<br>
> userinfo subcomponent and its "@" delimiter (Section 4.2 of [HTTP]).<br></blockquote><div><br></div><div>It's an nginx feature, so we can't really get in its way.</div><div><br></div><div>We should limit ourselves to fixing proxy_params, and that's not going to</div><div>be backported to trixie so I suggest to just wait for nginx 1.30 to be in debian,</div><div>and then use $is_request_port.</div><div><br></div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
Other HTTP servers, do not work like this. For example:<br>
<br>
* Apache HTTPD appears to ignore the incoming Host header field<br>
entirely when the authority is present in the request line;<br>
* ditto for Traefik;<br>
* ditto for Caddy<br>
* HA proxy triggers a HTTP 400 when receiving a host-ambiguous request;<br>
* ditto for uWSGI.<br>
<br>
For HTTP/2, the expected behavior is clearly to reject the request<br>
when both "Host" and ":authority" are used but are not consistent<br>
as per RFC 9113 section 9.3.1 [5] (HTTP/2):<br>
<br>
> A server SHOULD treat a request as malformed if it contains a<br>
> Host header field that identifies an entity that differs<br>
> from the entity in the ":authority" pseudo-header field.<br>
<br>
Ditto for HTTP/3, as per RFC 9114 section 4.3.1 [6] as far as<br>
I understand:<br>
<br>
> If both fields are present, they MUST contain the same value.<br>
><br>
> [..]<br>
><br>
> An HTTP request that omits mandatory pseudo-header fields or<br>
> contains invalid values for those pseudo-header fields is<br>
> malformed.<br>
<br>
[1] <a href="https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/" rel="noreferrer" target="_blank">https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/</a><br>
[2] <a href="https://www.rfc-editor.org/rfc/rfc9112.html#name-absolute-form" rel="noreferrer" target="_blank">https://www.rfc-editor.org/rfc/rfc9112.html#name-absolute-form</a><br>
[3] <a href="https://www.rfc-editor.org/rfc/rfc9110.html#name-intermediaries" rel="noreferrer" target="_blank">https://www.rfc-editor.org/rfc/rfc9110.html#name-intermediaries</a><br>
[4] <a href="https://www.rfc-editor.org/rfc/rfc9112.html#section-3.2" rel="noreferrer" target="_blank">https://www.rfc-editor.org/rfc/rfc9112.html#section-3.2</a><br>
[5] <a href="https://www.rfc-editor.org/rfc/rfc9113.html#section-8.3.1" rel="noreferrer" target="_blank">https://www.rfc-editor.org/rfc/rfc9113.html#section-8.3.1</a><br>
[6] <a href="https://www.rfc-editor.org/rfc/rfc9114.html#section-4.3.1" rel="noreferrer" target="_blank">https://www.rfc-editor.org/rfc/rfc9114.html#section-4.3.1</a><br>
_______________________________________________<br>
Pkg-nginx-maintainers mailing list<br>
<a href="mailto:Pkg-nginx-maintainers@alioth-lists.debian.net" target="_blank">Pkg-nginx-maintainers@alioth-lists.debian.net</a><br>
<a href="https://alioth-lists.debian.net/cgi-bin/mailman/listinfo/pkg-nginx-maintainers" rel="noreferrer" target="_blank">https://alioth-lists.debian.net/cgi-bin/mailman/listinfo/pkg-nginx-maintainers</a><br>
</blockquote></div></div>