[security advisory] $http_host vs $host

Francis Daly francis at daoine.org
Tue Mar 10 21:09:00 UTC 2015

On Mon, Mar 09, 2015 at 08:56:28PM +0200, Gena Makhomed wrote:
> On 09.03.2015 19:25, Francis Daly wrote:

Hi there,

thank you for the explanation.

> >It is true that $http_host is completely controlled by the client, and
> >$host is mostly controlled by the client. It is true that they can have
> >different values. I do not see that the difference is a security issue
> >in this case.

> server {
>    listen 443 ssl;
>    server_name private.example.com;
>    location / {
>      auth_basic "closed site";
>      auth_basic_user_file conf/htpasswd;
>      proxy_set_header  Host $http_host;
>      proxy_pass http://backend;
>    }
> }
> server {
>    listen 443 ssl;
>    server_name public.example.com;
>    location / {
>      proxy_set_header  Host $http_host;
>      proxy_pass http://backend;
>    }
> }
> in such configuration anybody can bypass nginx auth_basic restriction
> and access content from private.example.com without any login/password:

You are correct.

It is possible to construct a scenario where the difference between
$http_host and $host matters from a security perspective.

It is also possible to construct a scenario where the difference between
$http_host and $host matters from a correctness perspective.

The "common" redmine configuration appears to be one of the latter,
if nginx is not run on port 80 (such as, by using the original example
config and running nginx as non-root).

If nginx runs on port 80, then everything probably Just Works.

I suspect that if the "proxy_set_header Host" is omitted entirely, then
the default "proxy_redirect" probably means that everything Just Works,
whatever port nginx listens on. And the current example config does just
that, so it's all good.

> for fastcgi_pass such bug can be fixed only by using two nginx
> servers - first for frontend, and second for backend,
> because nginx send to fastcgi value of $http_host

I think it may be possible for the fastcgi application to avoid that
confusion -- nginx sends the http headers plus whatever is configured to
be sent. So, for creation of "redirect" urls back to the client,
HTTP_HOST may be sensible to use; but for anything where the nginx
server{} chosen matters, sending $server_name or $host from nginx in a
well-known fastcgi_param is probably a better option.

> >>So, $host must be used always with proxy_pass instead of $http_host.
> >
> >If the upstream server would do anything security-relevant with the Host:
> >header that it gets from nginx, it would do exactly the same with the
> >Host: header that it would get from the client directly, no?
> No.

You are correct, thanks.

I had forgotten about the absolute form of request to nginx, which
becomes origin form when nginx speaks to upstream.

nginx will see the full request and choose the server{} block to use, and
set $host and $server_name and $http_host appropriately. If upstream cares
about the difference between them, then nginx must be configured to send
the correct one (or none at all).

nginx will send no more than one in the Host: header, while the client
can send different names in Host: and in the request line.


Francis Daly        francis at daoine.org

More information about the nginx mailing list