contao/standard-edition

ConflictingHeadersException with AppCache

Closed this issue ยท 33 comments

Since a few weeks we occasionally get the following exception:

Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException
The request headers contain conflicting information regarding the origin of this request.

The request has both a trusted Forwarded header and a trusted Client IP header, conflicting with each other with regards to the originating IP addresses of the request. This is the result of a misconfiguration. You should either configure your proxy only to send one of these headers, or configure Symfony to distrust one of them.

The issue only appears on Opera Mini on some generic smartphones, but Opera version 4 until 18 all cause this. If this is really something to be configured in Symfony, I think it should be done in our standard edition (Symfony config or AppCache), because I'm probably not the only one affected.

Anyone have a clue what to do?

The information does not write about Symfony AppCache, but that probably is the same and we should just whitelist localhost?

I don't think we should apply any general solution without having understood the issue in full.

Should probably open this issue on symfony/symfony because it does not seem to be related to anything we use for Contao in specific?

Would be interesting to see the HTTP headers of the request which causes the issue. However, I cannot reproduce the issue in Opera 12.

Could this be the result of a wrong request? If the FORWARDED and X_FORWARDED_FOR headers are both set and have different values, the exception is thrown. But if the application does not use any kind of reverse proxy, the misconfiguration must be in the original request already, right?

The application always uses a reverse proxy due to our Symfony AppCache reverse proxy.

Here's one of the original headers. I've changed the IP for anonymity reasons, but interestingly it's the same in both headers.

Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
Accept-Encoding: gzip, deflate
Accept-Language: de,en;q=0.9
Connection: Keep-Alive
Device-Stock-Ua: Mozilla/5.0 (Linux; Android 4.4.2; BLOOM Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36
Forwarded: for="10.10.10.10:34161"
Host: www.example.com"
User-Agent: Opera/9.80 (Android; Opera Mini/18.0.2254/37.8814; U; de) Presto/2.12.423 Version/12.16
X-Forwarded-For: 10.10.10.10
X-Operamini-Features: httpping, advanced, pingback, routing, file_system, camera, touch, viewport, folding, download
X-Operamini-Phone: Android #
X-Operamini-Phone-Ua: Mozilla/5.0 (Linux; Android 4.4.2; BLOOM Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36"

It appears the IP is originating from opera-mini.net domain (reverse lookup), so maybe Opera does have a built-in reverse proxy to improve mobile speed that causes this issue?

These are the two interesting lines:

Forwarded: for="10.10.10.10:34161"
X-Forwarded-For: 10.10.10.10

Symfony will compare 10.10.10.10:34161 against 10.10.10.10 and since they do not match, the exception is thrown.

I'm aware of that. But I don't think Opera's behavior is wrong. And we can't decide to mistrust one of the headers. We just should be able to define which one is preferred. Feels like a general Symfony issue?

Apparently it's the very same for Chrome's Data Saver.

I just had to fight the same Problem within our project. The solution was quite simple maybe it can give you a hint to find out the problem with your configuration:

There are two Headers that can be set when you (or your project) is sitting behind a Proxy/Load Balancer:

  • Forwarded (RFC)
  • X-Forwarded-For

The Opera Mini Browser whith extreme data saving mode enabled for example makes your mobile browser sit behind an opera proxy which sets both of these headers.
Our server sat behind a misconfigured load balancing Proxy which just set one those.
The result was, that our symfony app had to deal with two forwarded header that were different (because one held the ip of the opera proxy AND ours and the other held just the ip adress from the opera proxy).

Request.php (line 821) says:

if ($hasTrustedForwardedHeader && $hasTrustedClientIpHeader && $forwardedClientIps !== $xForwardedForClientIps) {
    throw new ConflictingHeadersException('The request has both a trusted Forwarded header and a trusted Client IP header, conflicting with each other with regards to the originating IP addresses of the request. This is the result of a misconfiguration. You should either configure your proxy only to send one of these headers, or configure Symfony to distrust one of them.');
}

which means that the exception is thrown when the IP-Adresses from the two headers differ.

These are the two interesting lines:

Forwarded: for="10.10.10.10:34161"
X-Forwarded-For: 10.10.10.10

and this should not be quite true because symfony does extract the port number from the ip adress.

@Advialance Thanks for the clarification. How did you finally solve the problem?

@leofeyer I chose the simple way. The Forwarded Header is not needed at any Point by the Webserver. Within our Application we are just using the X-Forwarded-For Header.
You could just

unset($_SERVER["HTTP_FORWARDED"])

Above could cause a problem if a proxy sets the Forwarded Header but not the X-Forwarded-For as you could lose some information if you just unset that variable so it would be better to combine the information in

$_SERVER["HTTP_FORWARDED"]

into

$_SERVER["HTTP_X_FORWARDED_FOR"]

or vise versa and then unset the unnecessary one at the beginning of your Application.

That was my quick and dirty fix that works well but you can make sure that both of the Headers are always the same from within your Proxy Configuration, too.
In the end you just have to make sure that both of the Headers either inherit the same Information about the IP-Adresses the Request ran through or make sure that just one of the Headers are set.

AFAIK, you can also tell Symfony to ignore one of the headers.

Let's wait for an answer here: symfony/symfony#7034 (comment)

@aschempp I wonder if you posted the correct headers? The getClientIps() method makes an early return here with your headers and does not even get to the point where the exception is thrown.

Or did you configure any trusted proxies?

I'm basically using our standard edition, not sure if AppCache has any effect.

Ok, so here is the output of $forwardedClientIps and $xForwardedForClientIps when the error occurs:

Array
(
    [0] => 46.127.15.xxx
)

Array
(
    [0] => 141.0.14.174
    [1] => 141.0.14.174
    [2] => 46.127.15.xxx
)
Array
(
    [0] => 46.127.15.xxx
)

Array
(
    [0] => 82.145.219.118
    [1] => 82.145.219.118
    [2] => 46.127.15.xxx
)

141.0.14.174 resolves to z06-03-03.opera-mini.net and 82.145.219.118 resolves to z01-09-02.opera-mini.net. Apparently, Opera adds the proxy IPs to the X_FORWARDED_FOR header but not to the FORWARDED header, which is why the two arrays differ.

Jep. And that's something we can't control, and we can't correctly configure reverse proxy. So someone needs to unify them I would say? Or generally ignore X_FORWARDED_FOR (the old config) if there's a new FORWARDED header?

Oh look at this: http://forums.opera.com/discussion/1875157/conflicting-headers/p1 ๐Ÿ˜„

Interesting, an Opera employee seems to respond ๐Ÿ˜Ž

Or generally ignore X_FORWARDED_FOR (the old config) if there's a new FORWARDED header?

Sounds reasonable. However, this requires to adjust the app.php file:

https://symfony.com/doc/current/request/load_balancer_reverse_proxy.html#my-reverse-proxy-sends-x-forwarded-for-but-does-not-filter-the-forwarded-header

No I don't think that's the solution. They are always talking about trusted proxies, but that is not the case here. We don't know and don't trust the Opera proxies.

The link is about distrusting either the Forward or the X-Forward-For header.

Ideally, you would configure this in your proxy. If this is not possible, you can tell Symfony to distrust the Forwarded header, while still trusting your proxy's X-Forwarded-For header.

I don't know, but I have no idea what header to trust or distrust...

We should distrust the X-Forward-For header as soon as there is a Forward header (as you suggested above).

I receive that error on android 5.1.1 within iron-browser v53.0....(17. Nov 2016).

[2016-12-08 13:10:23] app.CRITICAL: An exception occurred. {"exception":"[object] (Symfony\\Component\\HttpKernel\\Exception\\BadRequestHttpException(code: 0): The request headers contain conflicting information regarding the origin of this request. at /.../vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpKernel.php:71, Symfony\\Component\\HttpFoundation\\Exception\\ConflictingHeadersException(code: 0): The request has both a trusted Forwarded header and a trusted Client IP header, conflicting with each other with regards to the originating IP addresses of the request. This is the result of a misconfiguration. You should either configure your proxy only to send one of these headers, or configure Symfony to distrust one of them. at /.../app/bootstrap.php.cache:729)"} []

@aschempp guys did you resolve this somehow?

Could it be that Symfony fixed this somehow? I haven't seen the exception in a while.

I think this might have fixed it: symfony/symfony#21849

I think so, too. I'm closing this ticket then for the time being.