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

