Skip to content

[FrameworkBundle] allow configuring trusted proxies using semantic configuration #37357

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

Merged
merged 1 commit into from
Jun 23, 2020

Conversation

nicolas-grekas
Copy link
Member

@nicolas-grekas nicolas-grekas commented Jun 19, 2020

Q A
Branch? master
Bug fix? no
New feature? yes
Deprecations? -
Tickets -
License MIT
Doc PR -

On top of the improved DX this should provide, this PR (and #37351) will allow removing the corresponding lines from index.php & recipes.

Using values:

framework:
    trusted_proxies: '127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16'
#or
    trusted_proxies: '%env(TRUSTED_PROXIES)%'

trusted_headers is similar but is an array of headers to trust.

framework:
    # that's the defaults already
    trusted_headers: ['x-forwarded-all', '!x-forwarded-host', '!x-forwarded-prefix']

@nicolas-grekas nicolas-grekas force-pushed the trusted-proxy branch 5 times, most recently from 299022a to 1fb519f Compare June 20, 2020 12:49
@nicolas-grekas
Copy link
Member Author

PR is ready.

@nicolas-grekas
Copy link
Member Author

(rebased)

@@ -242,6 +243,11 @@ public function load(array $configs, ContainerBuilder $container)
$container->setParameter('kernel.default_locale', $config['default_locale']);
$container->setParameter('kernel.error_controller', $config['error_controller']);

if (isset($config['trusted_proxies'], $config['trusted_headers'])) {
$container->setParameter('kernel.trusted_proxies', $config['trusted_proxies']);
Copy link
Member

Choose a reason for hiding this comment

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

Can we explode here instead of at runtime? We might also want to trim?

Copy link
Member Author

Choose a reason for hiding this comment

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

we can't really if we want to accept env vars here

Copy link
Member Author

Choose a reason for hiding this comment

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

trim() added, at runtime: I tried, but we don't support csv-parsed env vars in array-node definitions. I'm sure that could be done, but that'd require a lot of work on the Config component before. For now at least, we need to support string+explode.

@fabpot
Copy link
Member

fabpot commented Jun 23, 2020

Thank you @nicolas-grekas.

@fabpot fabpot merged commit a3900af into symfony:master Jun 23, 2020
@nicolas-grekas nicolas-grekas deleted the trusted-proxy branch June 23, 2020 09:14
@goetas
Copy link
Contributor

goetas commented Jun 28, 2020

Sorry for arriving this late to this pull request, but is this somehow related to the security issue in 3.3? I'm talking about https://symfony.com/blog/fixing-the-trusted-proxies-configuration-for-symfony-3-3 where the same parameter was removed from the configurations and now is being added back?

@nicolas-grekas
Copy link
Member Author

This PR builds on the knowledge gathered in the blog post and the related issues you linked. It doesn't reintroduce the original issue.

  1. Trusted headers are configurable now, and have a safe default value (not trusting x-forwarded-host)
  2. No proxies are trusted by default,
  3. Runtime configuration happens very early when handling the request, earlier than HttpCache when it's enabled.
    Did you spot an issue?

@goetas
Copy link
Contributor

goetas commented Jun 28, 2020

  • from the DX point of view this change is much better, it removes a lot of "mysterious" code from the index.php file
  • I was expecting some reference to the initial security issue since the feature was already there, and was confused in seeing it back
  • from the implementation point of view it introduces even more coupling with the request object (in this case calling methods on it father than just blindly forwarding it to the handler).
    a better way could be having an event in the preBoot (but i guess the event dispatcher is not available yet), or a dedicated interface that can be used to hook into the boot process, and having a dedicated class providing the feature of "configuring some global state". in this way other people get a way to hook by them self into the process..
    but I guess that might be out of scope for this change

@nicolas-grekas
Copy link
Member Author

nicolas-grekas commented Jun 28, 2020

You're correct. Providing extensibility here is a separate task, that'd need a use case and someone to implement it. OSS dynamics: future will tell if/why and how we should do it.

@goetas
Copy link
Contributor

goetas commented Jun 28, 2020

I'm perfectly aware of how OSS works, it is just that symfony was often looking a bit more in the future and having static method calls here and there rarely has been a good idea.

Edit: I'm aware of how OSS works, do not need reminders, and in my last comment I did not ask you to change anything in the implementation.

@mpdude
Copy link
Contributor

mpdude commented Jun 30, 2020

May I ask why the default is (and previously was, elsewhere) not to trust X-Forwarded-Host?

I understand that trusting this header has security implications, but if I am not misreading the change made here the prerequisite for evaluating the header is to configure a trusted proxy in the first place? And once you do that (because you have a proxy in place), wouldn't you usually want to also trust X-Forwarded-Host?

Also, on https://symfony.com/doc/master/deployment/proxies.html, the example configuration shown configures the proxy's IP address and uses Request::HEADER_X_FORWARDED_ALL. It does not even mention not trusting X-Forwarded-Host.

@nicolas-grekas
Copy link
Member Author

@mpdude sure, check #20178

@mpdude
Copy link
Contributor

mpdude commented Jun 30, 2020

Thank you Nicolas!

Documentation has been re-arranged a bit since #20178, so I hope I did not miss anything that was more visible back then; also I followed the related doc PRs and hope I understood it all correctly.

We all 💯 that we must not trust any proxy or header by default.

David's intention back in #20178 was to make trusting proxies more safe by default by not having any default headers that would be trusted once you configure proxy IPs. He was referring to AWS ELBs, that (to my knowledge, up to the present day) do not set X-Forwarded-Host. Not having any defaults should force people to think through their setup and choose the right set of headers.

Fabien suggested not to have defaults optimized for the AWS case.

I think the current documentation is mostly in line with this: It describes to either provide the IP and use Request::HEADER_X_FORWARDED_ALL; or, for the AWS ELB case, to configure network security appropriately and then use Request::HEADER_X_FORWARDED_AWS_ELB in combination with trusting all IPs.

The default configuration provided in the FrameworkBundle (with this PR) or Flex/recipes in 5.1 however is a bit odd, as it leans towards the AWS use case (Request::HEADER_X_FORWARDED_ALL ^ Request::HEADER_X_FORWARDED_HOST == Request::HEADER_X_FORWARDED_AWS_ELB).

So, for setups with an Apache-based Reverse Proxy (my current use case) you need to explicitly configure the IP + explicitly configure x-forwarded-all/Request::HEADER_X_FORWARDED_ALL. Probably the same for Varnish.

For AWS ELB, you need network-level security config outside the scope of Symfony + trust all IPs.

Bottom line:

It seems to me the defaults

  • are not right for the Apache/Varnish case
  • the AWS ELB case requires careful extra steps anyway
  • are not "more safe" than any other values (what about X-Forwarded-For, for example?)
  • do not follow David's suggestion (to my understanding).

Maybe BC prevents us from changing this anyway, but what about going without trusted headers or using x-forwarded-all?

@nicolas-grekas
Copy link
Member Author

nicolas-grekas commented Jun 30, 2020

Side note: we often get security reports from ppl that missed that trusted_hosts exists.
But ppl are lazy (I put myself in the bag). Not trusting any headers by default won't mean they'll think about all this. Instead, they'll copy/paste some blogpost/article. We all do this.
This means the solution is more in the documentation than in the code.
For the code part, this PR doesn't aim to change the preexisting default, precisely because it was not the topic.
If you want to lead an improvement on this, please open a separate issue (or better: PR).

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.

8 participants