So I was tasked with researching a problem that was identified by an Acunetix web security scan.
I can’t post more than two links yet as I don’t have enough reputation, but the skeletonscribe reference on the first of the above links goes into more detail about the exploit and seems to be the main source on the subject.
The suggested workarounds suggest the use of ‘dummy’ default virtual hosts and applications using
SERVER_NAME instead of using the Host header. These suggestions are great when talking about a true origin server, but not so much when you are using a reverse proxy.
Our web site that is being scanned by Acunetix is set up as an application server sitting behind a reverse proxy. In our case, the reverse proxy is iPlanet, but I have confirmed the same behaviour in Apache 2.4.7 with
mod_proxy. Here is how it works, and why it triggers the Acunetix scan.
When a request is made using an absolute URI (As the scanner does), the server is, according to RFC spec, meant to ignore the Host header and instead use the host that is within the absolute URI. This is the behaviour that we see and as a result, the correct virtual host is selected, even if the Host header has an incorrect/malicious value. So far so good.
The problem arises when the reverse proxy then passes this request on to the back-end origin server. When it does this, it passes the original Host header along with the request. If the Host header has an incorrect or malicious value, this will then get passed back to the origin server. If the origin server does not implement virtual hosts, then it does not need to verify that the Host header is correct.
The result of this is that any application running on the origin web/app server that tries to use the value of the Host header will be using the malicious host value. Using the
SERVER_NAME is of no use in this instance as the server name (or true host) of the site does not get forwarded by the reverse proxy and the origin server still responds with the Host header value. Using
UseCanonicalName and the
ServerName directive here is also of no use, as you need the server name from the original request, which could be more than one value.
This ‘attack’ was identified in 2013, but I can’t find a whole lot of information on it that doesn’t just re-iterate the details from the above references. It’s not a common problem because HTTP clients do not send absolute URI’s unless they are talking to forward proxy servers. This means that all regular requests will be relative URI’s and this problem then goes away (Malicious Host headers then get stuck at the ‘dummy’ or default virtual host on the reverse proxy).
I did create a workaround, whereby the reverse proxy actively sets the Host header to the
SERVER_NAME before the request is sent to the back-end. This works, but I worry if it may be a solution that goes against bet practices/standards. Would setting the Host header like this break something? We brainstormed this, but I can’t think of a scenario where it would. I will be opening a call to Oracle tomorrow to get feedback from them.
It seems to me that this may be such a fringe case, that it’s just an oversight in the two web server products that I tried, but I can’t imagine something would go so long without being addressed. I must be missing something.
Sorry it’s so long, but, any ideas? 🙂 The ‘question’ part of this essay would be this:
- Would my workaround above be a good solution or would it break things?
- Is there a known solution for this problem?
Your reverse proxy is supposed to be correcting the Host: header for you by default. That it doesn’t do so is a bug and should be reported to its developer(s).
From RFC 7230 section 5.4:
When a proxy receives a request with an absolute-form of request-target, the proxy MUST ignore the received Host header field (if any) and instead replace it with the host information of the request-target. A proxy that forwards such a request MUST generate a new Host field-value based on the received request-target rather than forward the received Host field-value.
Note that the previous version of this RFC, RFC 2616, was much less specific. It only said:
An HTTP/1.1 proxy MUST ensure that any request message it forwards does contain an appropriate Host header field that identifies the service being requested by the proxy.
The behavior of your proxy cannot be said to conform to either the old or the new specification.
In the meantime, your workaround is fine, since it introduces the correct behavior.
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.