Skip to content

[HttpFoundation] Warning when request has both Forwarded and X-Forwarded-For #18688

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 11 commits into from

Conversation

magnusnordlander
Copy link
Contributor

@magnusnordlander magnusnordlander commented May 2, 2016

Q A
Branch? 2.7
Bug fix? yes
New feature? no
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets
License MIT
Doc PR symfony/symfony-docs#6526

Emit a warning when a request has both a trusted Forwarded header and a trusted X-Forwarded-For header, as this is most likely a misconfiguration which causes security issues.

Forwarded header and a trusted X-Forwarded-For
header, as this is most likely a misconfiguration
which causes security flaws.
$hasTrustedClientIpHeader = self::$trustedHeaders[self::HEADER_CLIENT_IP] && $this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP]);

if ($hasTrustedForwardedHeader && $hasTrustedClientIpHeader) {
trigger_error('The request has both a trusted Forwarded header and a trusted Client IP header. This is likely a misconfiguration. You should either configure your proxy only to send one of these headers, or configure Symfony to distrust one of them. When both headers are set and trusted, this method returns only IPs from the Forwarded header.', E_USER_WARNING);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if we could instead throw an exception here. What do you think? Any use case where setting both would be legitimate?

Copy link
Contributor Author

@magnusnordlander magnusnordlander May 3, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could be legitimate as a legacy measure from proxies, if both headers contain the same information, I suppose.

That can be detected of course, I mean, it's just a matter of parsing both headers and see if they have the same IPs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll update this PR to do that, because we probably don't want neither to warn nor throw exceptions if a proxy sends both headers for backwards compatibility.

@magnusnordlander
Copy link
Contributor Author

@fabpot: I have now updated the PR to throw exceptions instead, and to not do so when both the Forwarded and X-Forwarded-For header agree with each other.

I am a bit worried about the fact that the very commonly used getClientIp method now throws exceptions when users previously did not expect it to, though. I mean, the request is inconsistent, so clearly we need to do something, but this can happen when the user least expects it to. Should I perhaps "validate" the request early on in the request lifecycle, to make sure that if we're going to throw, we throw early?

}
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.');
} elseif (!$hasTrustedForwardedHeader && !$hasTrustedClientIpHeader) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be simple if cause you throw exception in if before it.

xabbuh added a commit to symfony/symfony-docs that referenced this pull request May 9, 2016
…s to the Forwarded header (magnusnordlander)

This PR was squashed before being merged into the 2.7 branch (closes #6526).

Discussion
----------

Documented how to configure Symfony correctly with regards to the Forwarded header

| Q             | A
| ------------- | ---
| Doc fix?      | yes
| New docs?     | no
| Applies to    | >2.7
| Fixed tickets |

Ref: symfony/symfony#18688

Commits
-------

87ab598 Documented how to configure Symfony correctly with regards to the Forwarded header
@fabpot
Copy link
Member

fabpot commented May 13, 2016

@magnusnordlander I agree with you that we should bail out as early as possible and consistently.

@@ -1930,4 +1922,35 @@ private function isFromTrustedProxy()
{
return self::$trustedProxies && IpUtils::checkIp($this->server->get('REMOTE_ADDR'), self::$trustedProxies);
}

private function normalizeAndFilterClientIps($clientIps, $ip)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would typehint the first argument as array

@magnusnordlander
Copy link
Contributor Author

I'll give some consideration to where to put this validation, and have an updated PR ready tomorrow (also fixing the other two things, of course).

@nicolas-grekas
Copy link
Member

Status: needs work

@fabpot
Copy link
Member

fabpot commented Jun 15, 2016

@magnusnordlander Do you have time in the next few days to update this PR?

@magnusnordlander
Copy link
Contributor Author

I'll have time this Friday.

@magnusnordlander
Copy link
Contributor Author

So, the code is updated. The remaining build error is because the composer.json of HttpKernel and FrameworkBundle needs to be updated to require the version of HttpFoundation and HttpKernel respectively, that this is released in. I don't know how to handle that without actually making a release. Any help there?

@magnusnordlander
Copy link
Contributor Author

Ping @fabpot

*
* @author Magnus Nordlander <magnus@fervo.se>
*/
class ValidateRequestClientIpListener implements EventSubscriberInterface
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about naming this class ValidateRequestListener to allow adding more checks in the future?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have a generic response_listener, so naming it request_listener might be an option as well (not sure about possible conflicts with existing end-user listeners though).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should think that validate_request_listener has less potential for conflict

@@ -46,5 +46,9 @@
<argument type="service" id="request_stack" />
<tag name="kernel.event_subscriber" />
</service>

<service id="validate_request_client_ip_listener" class="Symfony\Component\HttpKernel\EventListener\ValidateRequestClientIpListener">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we rename the class as I suggest, this should be renamed accordingly (validate_request_listener).

@fabpot
Copy link
Member

fabpot commented Jun 28, 2016

👍

@fabpot
Copy link
Member

fabpot commented Jun 29, 2016

Thank you @magnusnordlander.

@fabpot fabpot closed this in 2d37230 Jun 29, 2016
HeahDude added a commit to HeahDude/symfony that referenced this pull request Jun 29, 2016
fabpot added a commit that referenced this pull request Jun 29, 2016
This PR was merged into the 2.7 branch.

Discussion
----------

fixed HttpKernel dependencies after #18688

| Q             | A
| ------------- | ---
| Branch?       | 2.7
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | ~
| License       | MIT
| Doc PR        | ~

Commits
-------

f809f3e fixed HttpKernel dependencies after #18688
nicolas-grekas added a commit that referenced this pull request Jun 29, 2016
…pKernel (nicolas-grekas)

This PR was merged into the 2.7 branch.

Discussion
----------

[HttpKernel] Inline ValidateRequestListener logic into HttpKernel

| Q             | A
| ------------- | ---
| Branch?       | 2.7
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #18688 #19216
| License       | MIT
| Doc PR        | -

I propose to inline the listener introduced in #18688 into HttpKernel.

Commits
-------

9d3ae85 [HttpKernel] Inline ValidateRequestListener logic into HttpKernel
nicolas-grekas added a commit that referenced this pull request Jun 29, 2016
* 2.7:
  [HttpKernel] Inline ValidateRequestListener logic into HttpKernel
  fixed HttpKernel dependencies after #18688

Conflicts:
	src/Symfony/Component/HttpKernel/composer.json
nicolas-grekas added a commit that referenced this pull request Jun 29, 2016
* 2.8:
  [FrameworkBundle] Fix fixtures
  [HttpKernel] Inline ValidateRequestListener logic into HttpKernel
  fixed HttpKernel dependencies after #18688

Conflicts:
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.txt
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.txt
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.txt
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.txt
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_1.txt
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_2.txt
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/parameter.txt
	src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_collection_1.txt
	src/Symfony/Bundle/FrameworkBundle/composer.json
	src/Symfony/Component/HttpKernel/composer.json
nicolas-grekas added a commit that referenced this pull request Jun 29, 2016
* 3.0:
  [FrameworkBundle] Fix fixtures
  [HttpKernel] Inline ValidateRequestListener logic into HttpKernel
  fixed HttpKernel dependencies after #18688

Conflicts:
	src/Symfony/Component/HttpKernel/HttpKernel.php
	src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php
nicolas-grekas added a commit that referenced this pull request Jun 29, 2016
* 3.1:
  [Form] fixed ChoiceTypeTest after #17822
  [DoctrineBridge] fixed DoctrineChoiceLoaderTest by removing deprecated factory
  [ci] Upgrade phpunit wrapper deps
  [FrameworkBundle] Fix fixtures
  [HttpKernel] Inline ValidateRequestListener logic into HttpKernel
  fixed HttpKernel dependencies after #18688
@leandigital
Copy link

Late to the party here, but I just experienced the exception after upgrading to 2.8.8 and landed on this page. The server is behind Akamai, and looks like it's sending both Forwarded and X-Forwarded-For headers. I checked the new documentation and tried disallowing one of the headers, but it didn't help and the error didn't go away. Any ideas why and how do I only allow one of those headers? Here is the snippet I used: Request::setTrustedHeaderName(Request::HEADER_FORWARDED, null);

@ninsuo
Copy link

ninsuo commented Jul 27, 2016

Well some of our members use proxies (Forwarded) and we use reverse proxies (X-Forwarded-For), as a result, boom. Do I miss something?

@magnusnordlander
Copy link
Contributor Author

@iSyndicate So, first of all, it seems odd that Akamai is sending you different information about the provenance of the request in the different headers. That's not been my experience with them, and I suspect might be a configuration issue. However, you should, as you have attempted, be able to fix that by distrusting one of the headers, and that snippet is indeed correct. In which file, and where, did you put that snippet?

@ninsuo: Well, yes. You can't trust the information coming from random strangers (or in this case rather, random clients) on the Internet. Ideally your reverse proxy would be filtering out the untrusted Forwarded header (because, since you have marked it as a trusted proxy, you are relying on it for accurate information on the provenance of the request, and the Forwarded header coming from your clients is not trustworthy), but if that is not possible, you should make sure Symfony distrusts the Forwarded header, as detailed here: http://symfony.com/doc/current/request/load_balancer_reverse_proxy.html#my-reverse-proxy-sends-x-forwarded-for-but-does-not-filter-the-forwarded-header

@ninsuo
Copy link

ninsuo commented Jul 29, 2016

Thank you for clarification.

But as everybody can set any header I don't see any point of using them as a reliable information. We do trust and use only the headers our nginx is setting, no matter what our members are setting.

We can use Referer header (as well as lots of others) to flag client as "using a proxy" and giving him a lesser trust score (more chances to get 3DSecure, MFA, ...) and removing it is quite inconvenient.

@hightoxicity
Copy link

hightoxicity commented Jul 29, 2016

@magnusnordlander

You can not trust X-Forwarded, Forwarded, both of them, your system engineer is in charge of reading remoteip_addr on the first front node of your infrastructure and provide you (to your framework) the value read in a secure way that you can trust (according together), I think about a dedicated header, your system engineer sets/overrides in all cases. You can keep Forwarded and X-Forwarded headers for information and to decorate... I do not see very well where the Symfony framework has to be intelligent on such security problem.

@magnusnordlander
Copy link
Contributor Author

This particular piece of code only runs when you have configured Symfony to trust a proxy server. By default, Symfony doesn't trust any proxy server, and does not consider X-Forwarded-For nor Forwarded to contain reliable information on the Client IP.

fabpot added a commit that referenced this pull request Mar 22, 2017
…:setTrustedProxies() - deprecate not setting it (nicolas-grekas)

This PR was merged into the 3.3-dev branch.

Discussion
----------

[HttpFoundation] Add $trustedHeaderSet arg to Request::setTrustedProxies() - deprecate not setting it

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | yes
| Tests pass?   | no tests yet
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

Follow up of #18688

PR adds a second `$trustedHeaderSet` argument to `Request::setTrustedProxies()`, can be either `Request::HEADER_FORWARDED` or `Request::HEADER_X_FORWARDED_ALL` to set which header to trust from your proxies - the idea being that without this info, one will get some `ConflictingHeadersException`, but those may be lost in the logs.

Commits
-------

d3c9604 [HttpFoundation] Add $trustedHeaderSet arg to Request::setTrustedProxies() - deprecate not setting it
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants