[security advisory] $http_host vs $host

Gena Makhomed gmm at csdoc.com
Mon Mar 9 18:56:28 UTC 2015


On 09.03.2015 19:25, Francis Daly wrote:

>>>> Unsafe variable $http_host was used instead of safe one $host
>>>
>>> I'm not sure how $http_host is less safe than $host. It is proxy_pass'ed
>>> to the "real" redmine server as the Host header. That server must be
>>> able to handle it safely anyway, no?
>>
>> Such configuration allow to spoof nginx built-in server selection rules.
>> because nginx will use server name from request line, but will provide
>> to upstream completely different server name, from Host request header.
>
> 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:

GET https://public.example.com/top-secret.pdf HTTP/1.1
Host: private.example.com

nginx will use host name public.example.com for server selection,
and process request in second server, but send to backend
"Host: private.example.com" and relative URI in request line.

and backend will process such request as request to private.example.com

because backend server see only relative uri in request line,
and will use host name from Host: request header in this case.

=======================================================================

for proxy_pass such bug can be fixed just by using always
$host instead of $http_host in proxy_set_header Host directive.

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

bug cause:

fastcgi spec was created when only HTTP/1.0 exists
and don't know about absoluteURI in request line -
such feature was added in HTTP/1.1, after FastCGI spec.

>> 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.

1) http://tools.ietf.org/html/rfc7230#section-5.5

2) http://nginx.org/en/docs/http/ngx_http_core_module.html

$host
     in this order of precedence: host name from the request line, or 
host name from the “Host” request header field, or the server name 
matching a request

-- 
Best regards,
  Gena



More information about the nginx mailing list