diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index a66937add4..214209bedc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG ----- * Added `framework.http_cache` configuration tree + * Added `framework.trusted_proxies` and `framework.trusted_headers` configuration options 5.1.0 ----- diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index f644a57075..3711701565 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -85,6 +85,20 @@ class Configuration implements ConfigurationInterface ->beforeNormalization()->ifString()->then(function ($v) { return [$v]; })->end() ->prototype('scalar')->end() ->end() + ->scalarNode('trusted_proxies')->end() + ->arrayNode('trusted_headers') + ->fixXmlConfig('trusted_header') + ->performNoDeepMerging() + ->defaultValue(['x-forwarded-all', '!x-forwarded-host', '!x-forwarded-prefix']) + ->beforeNormalization()->ifString()->then(function ($v) { return $v ? array_map('trim', explode(',', $v)) : []; })->end() + ->enumPrototype() + ->values([ + 'forwarded', + 'x-forwarded-for', 'x-forwarded-host', 'x-forwarded-proto', 'x-forwarded-port', + 'x-forwarded-all', '!x-forwarded-host', '!x-forwarded-prefix', + ]) + ->end() + ->end() ->scalarNode('error_controller') ->defaultValue('error_controller') ->end() diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 38825befe4..f942548ff8 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -65,6 +65,7 @@ use Symfony\Component\Form\FormTypeExtensionInterface; use Symfony\Component\Form\FormTypeGuesserInterface; use Symfony\Component\Form\FormTypeInterface; use Symfony\Component\HttpClient\ScopingHttpClient; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface; use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; @@ -242,6 +243,11 @@ class FrameworkExtension extends Extension $container->setParameter('kernel.default_locale', $config['default_locale']); $container->setParameter('kernel.error_controller', $config['error_controller']); + if (($config['trusted_proxies'] ?? false) && ($config['trusted_headers'] ?? false)) { + $container->setParameter('kernel.trusted_proxies', $config['trusted_proxies']); + $container->setParameter('kernel.trusted_headers', $this->resolveTrustedHeaders($config['trusted_headers'])); + } + if (!$container->hasParameter('debug.file_link_format')) { $links = [ 'textmate' => 'txmt://open?url=file://%%f&line=%%l', @@ -2094,6 +2100,30 @@ class FrameworkExtension extends Extension } } + private function resolveTrustedHeaders(array $headers): int + { + $trustedHeaders = 0; + + foreach ($headers as $h) { + switch ($h) { + case 'forwarded': $trustedHeaders |= Request::HEADER_FORWARDED; break; + case 'x-forwarded-for': $trustedHeaders |= Request::HEADER_X_FORWARDED_FOR; break; + case 'x-forwarded-host': $trustedHeaders |= Request::HEADER_X_FORWARDED_HOST; break; + case 'x-forwarded-proto': $trustedHeaders |= Request::HEADER_X_FORWARDED_PROTO; break; + case 'x-forwarded-port': $trustedHeaders |= Request::HEADER_X_FORWARDED_PORT; break; + case '!x-forwarded-host': $trustedHeaders &= ~Request::HEADER_X_FORWARDED_HOST; break; + case 'x-forwarded-all': + if (!\in_array('!x-forwarded-prefix', $headers)) { + throw new LogicException('When using "x-forwarded-all" in "framework.trusted_headers", "!x-forwarded-prefix" must be explicitly listed until support for X-Forwarded-Prefix is implemented.'); + } + $trustedHeaders |= Request::HEADER_X_FORWARDED_ALL; + break; + } + } + + return $trustedHeaders; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php index 1244db0347..7f439bb572 100644 --- a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php +++ b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php @@ -95,10 +95,6 @@ class FrameworkBundle extends Bundle if ($this->container->getParameter('kernel.http_method_override')) { Request::enableHttpMethodParameterOverride(); } - - if ($trustedHosts = $this->container->getParameter('kernel.trusted_hosts')) { - Request::setTrustedHosts($trustedHosts); - } } public function build(ContainerBuilder $container) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd index 21ddccf342..7cc318a92a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd @@ -42,6 +42,9 @@ + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.php index 071572b33f..f0cd2b9350 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.php @@ -132,9 +132,9 @@ return static function (ContainerConfigurator $container) { ->tag('container.hot_path') ->set('http_cache.store', Store::class) - ->args([ - param('kernel.cache_dir').'/http_cache', - ]) + ->args([ + param('kernel.cache_dir').'/http_cache', + ]) ->set('url_helper', UrlHelper::class) ->args([ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index 523d8919c3..7e1ed5f731 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -30,10 +30,7 @@ class ConfigurationTest extends TestCase $processor = new Processor(); $config = $processor->processConfiguration(new Configuration(true), [['secret' => 's3cr3t']]); - $this->assertEquals( - array_merge(['secret' => 's3cr3t', 'trusted_hosts' => []], self::getBundleDefaultConfig()), - $config - ); + $this->assertEquals(self::getBundleDefaultConfig(), $config); } public function getTestValidSessionName() @@ -341,6 +338,13 @@ class ConfigurationTest extends TestCase 'http_method_override' => true, 'ide' => null, 'default_locale' => 'en', + 'secret' => 's3cr3t', + 'trusted_hosts' => [], + 'trusted_headers' => [ + 'x-forwarded-all', + '!x-forwarded-host', + '!x-forwarded-prefix', + ], 'csrf_protection' => [ 'enabled' => false, ], diff --git a/src/Symfony/Component/HttpKernel/CHANGELOG.md b/src/Symfony/Component/HttpKernel/CHANGELOG.md index 6bf20c948f..19f8d9f3bd 100644 --- a/src/Symfony/Component/HttpKernel/CHANGELOG.md +++ b/src/Symfony/Component/HttpKernel/CHANGELOG.md @@ -5,6 +5,8 @@ CHANGELOG ----- * made the public `http_cache` service handle requests when available + * allowed enabling trusted hosts and proxies using new `kernel.trusted_hosts`, + `kernel.trusted_proxies` and `kernel.trusted_headers` parameters 5.1.0 ----- diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index cf4329fc16..8f7ff96808 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -764,7 +764,17 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl $this->initializeBundles(); $this->initializeContainer(); - return $this->container; + $container = $this->container; + + if ($container->hasParameter('kernel.trusted_hosts') && $trustedHosts = $container->getParameter('kernel.trusted_hosts')) { + Request::setTrustedHosts($trustedHosts); + } + + if ($container->hasParameter('kernel.trusted_proxies') && $container->hasParameter('kernel.trusted_headers') && $trustedProxies = $container->getParameter('kernel.trusted_proxies')) { + Request::setTrustedProxies(\is_array($trustedProxies) ? $trustedProxies : array_map('trim', explode(',', $trustedProxies)), $container->getParameter('kernel.trusted_headers')); + } + + return $container; } /**