-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[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
Conversation
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); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
@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 |
} | ||
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) { |
There was a problem hiding this comment.
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.
…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
@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) |
There was a problem hiding this comment.
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
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). |
Status: needs work |
@magnusnordlander Do you have time in the next few days to update this PR? |
I'll have time this Friday. |
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? |
Ping @fabpot |
* | ||
* @author Magnus Nordlander <magnus@fervo.se> | ||
*/ | ||
class ValidateRequestClientIpListener implements EventSubscriberInterface |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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"> |
There was a problem hiding this comment.
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
).
👍 |
Thank you @magnusnordlander. |
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
…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
* 2.7: [HttpKernel] Inline ValidateRequestListener logic into HttpKernel fixed HttpKernel dependencies after #18688 Conflicts: src/Symfony/Component/HttpKernel/composer.json
* 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
* 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
* 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
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); |
Well some of our members use proxies (Forwarded) and we use reverse proxies (X-Forwarded-For), as a result, boom. Do I miss something? |
@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 |
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. |
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. |
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. |
…: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
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.