Closed rkevin-arch closed 4 years ago
Other than X-Forwarded-For
, the X-Forwarded-Host
header is usually single-valued. There is no standard, but the intention is to preserve the original Host
header value, as sent by the client, before forwarding the request to a back-end server. Multiple values make no sense here. Intermediate proxies that override or append to this header are broken, in my view. The same is true for X-Forwarded-Proto
.
And sure, you can forge this header. You can also forge a Host
header or any other header. It is the job of the front-end proxy/lb/gateway to ensure that only requests with a valid Host
header are accepted, and that the X-Forwarded-*
headers are all set to the correct values. If you do not do that, but still trust these headers in your back-end (directly or indirectly), then sure, you might have a problem there. This is true for any form of user input.
I'd gladly accept pull requests for Forwarded
support, though.
OK then, thanks for the explanation. For the record, that's the default behavior of Apache.
Yes, unfortunately, but they have a warning in their docs. Note that Apache also explicitly preserves and appends to user-provided values for that header, even on the first hop. Silently accepting the first value in bottle would actually increase the security risk. A broken and invalid hostname is better than a valid but malicious one, after all.
You see why the Apache default behavior is broken? There is no way to find the actual trustworthy original value of the Host
header behind an Apache proxy, because the first value of X-Forwarded-Host
can be forged. The header is rendered useless by Apache.
https://github.com/bottlepy/bottle/blob/332215b2b1b3de5a321ba9f3497777fc93662893/bottle.py#L1446
Other than being a security issue (since that header can be set by anyone), it doesn't work when you have two servers forwarding. Just like
X-Forwarded-For
, if there are multiple successive proxies,X-Forwarded-Host
would be a,
separated list of hostnames. This will break things likeredirect('/path')
, which would be turned into something like this:And then:
An easy fix would be to just add
.split(', ')[0]
, if you want me to create a pull request I can do so.