|
|
Message-ID: <5b72df78-eba4-48a0-8bc9-1620cbd8b554@free.fr>
Date: Fri, 22 May 2026 00:59:48 +0200
From: Gabriel Corona <gabriel.corona@...e.fr>
To: oss-security@...ts.openwall.com
Subject: Re: Host ambiguous requests through NGINX $host and
Debian's proxy_params
Host ambiguity could be a problem as well when using FastCGI,
SCGI or uWSGI before NGINX v1.29.5. This is fixed in
NGINX v1.29.5 and mitigated in Debian packages
(eg. 1.26.3-3+deb13u5).
For reference, here is official NGINX fastcgi_params before v1.29.5:
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REQUEST_SCHEME $scheme;
fastcgi_param HTTPS $https if_not_empty;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param REMOTE_USER $remote_user;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200
In this case as well, an attacker can set a HTTP_HOST parameter
which is not consistent with the value which was used for virtual
host routing.
For the following host-ambiguous HTTP request:
GET http://foo/ HTTP/1.1
User-Agent: UA
Host: bar
We have the following FastCGI variables before NGINX v1.29.5:
SERVER_NAME = foo
HTTP_HOST = bar <- from Host header field
We have the following FastCGI variables since NGINX v1.29.5:
SERVER_NAME = foo
HTTP_HOST = foo <- from request line
See this change:
Changes with nginx 1.29.5 04
Feb 2026
[...]
*) Bugfix: fixed setting HTTP_HOST when proxying to FastCGI, SCGI, and
uwsgi backends.
[...]
Relevant commit:
commit 71b18973b2b5ea29ed27b47fc0e619b4df533b60
Author: Andrew Clayton <a.clayton@...nx.com>
Date: Sat Dec 13 07:05:27 2025 +0000
FastCGI: ensure HTTP_HOST is set to the requested target host.
Previously, the HTTP_HOST environment variable was constructed from the
Host request header field, which doesn't work well with HTTP/2 and
HTTP/3 where Host may be supplanted by the ":authority" pseudo-header
field per RFC 9110, section 7.2. Also, it might give an incorrect
HTTP_HOST value from HTTP/1.x requests given in the absolute form, in
which case the Host header must be ignored by the server, per RFC 9112,
section 3.2.2.
The fix is to redefine the HTTP_HOST default from a protocol-specific
value given in the $host variable. This will now use the Host request
header field, ":authority" pseudo-header field, or request line target
URI depending on request HTTP version.
Also the CGI specification (RFC 3875, 4.1.18) notes
The server SHOULD set meta-variables specific to the protocol and
scheme for the request. Interpretation of protocol-specific
variables depends on the protocol version in SERVER_PROTOCOL.
Closes: https://github.com/nginx/nginx/issues/256
Closes: https://github.com/nginx/nginx/issues/455
Closes: https://github.com/nginx/nginx/issues/912
Closes: https://github.com/nginx/nginx/issues/912
Similar commits exist for uWSGI and SCGI (but bot for
ngx_http_proxy_module).
We can consider that the application code should rely on
SERVER_NAME (not HTTP_HOST according) to the CGI
specification [3]. However,
* the usage of HTTP_HOST in application code is quite prevalent
as well;
* SERVER_NAME/$server_name does not really do what we want
when using wildcard server names.
For example the URL reconstruction algorithm documented in
WSGI [1] is:
from urllib.parse import quote
url = environ['wsgi.url_scheme']+'://'
if environ.get('HTTP_HOST'):
url += environ['HTTP_HOST']
else:
url += environ['SERVER_NAME']
if environ['wsgi.url_scheme'] == 'https':
if environ['SERVER_PORT'] != '443':
url += ':' + environ['SERVER_PORT']
else:
if environ['SERVER_PORT'] != '80':
url += ':' + environ['SERVER_PORT']
url += quote(environ.get('SCRIPT_NAME', ''))
url += quote(environ.get('PATH_INFO', ''))
if environ.get('QUERY_STRING'):
url += '?' + environ['QUERY_STRING']
With this algorithm, HTTP_HOST takes precedence over SERVER_NAME.
The reconstructed URL is therefore vulnerable to host ambiguity
requests.
Similarly PSGI [2] says:
> SERVER_NAME, SERVER_PORT: When combined with SCRIPT_NAME and
> PATH_INFO, these keys can be used to complete the URL.
> Note, however, that HTTP_HOST, if present, should be used in
> preference to SERVER_NAME for reconstructing the request URL.
> SERVER_NAME and SERVER_PORT MUST NOT be empty strings, and are
> always required.
Debian uses the following workaround for older versions of NGINX:
fastcgi_param HTTP_HOST $host;
[1] https://peps.python.org/pep-3333/#url-reconstruction
[2] https://github.com/plack/psgi-specs/blob/master/PSGI.pod
[3] https://datatracker.ietf.org/doc/html/rfc3875#section-4.1.14
Regards,
--
Gabriel Corona
Download attachment "OpenPGP_signature.asc" of type "application/pgp-signature" (841 bytes)
Powered by blists - more mailing lists
Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.