bottlepy / bottle

bottle.py is a fast and simple micro-framework for python web-applications.
http://bottlepy.org/
MIT License
8.37k stars 1.46k forks source link

Bottle shouldn't use X_FORWARDED_HOST without checking #1215

Closed rkevin-arch closed 4 years ago

rkevin-arch commented 4 years ago

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 like redirect('/path'), which would be turned into something like this:

image

And then:

image

An easy fix would be to just add .split(', ')[0], if you want me to create a pull request I can do so.

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

defnull commented 4 years ago

I'd gladly accept pull requests for Forwarded support, though.

rkevin-arch commented 4 years ago

OK then, thanks for the explanation. For the record, that's the default behavior of Apache.

defnull commented 4 years ago

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.