From 6bd4b4a5a3283c36134eda2371210a9ac3b35a25 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 3 Sep 2024 10:21:02 +0200 Subject: [PATCH] [HttpFoundation] Add `PRIVATE_SUBNETS` as a shortcut for private IP address ranges to `Request::setTrustedProxies()` --- .../DependencyInjection/Configuration.php | 6 ++-- .../FrameworkExtensionTestCase.php | 2 +- .../Component/HttpFoundation/CHANGELOG.md | 1 + .../Component/HttpFoundation/Request.php | 24 ++++++++------ .../HttpFoundation/Tests/RequestTest.php | 31 +++++++++++++------ 5 files changed, 41 insertions(+), 23 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index ed7aff37414b1..44bbe6718b2c7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -111,10 +111,10 @@ public function getConfigTreeBuilder(): TreeBuilder ->beforeNormalization()->ifString()->then(fn ($v) => [$v])->end() ->prototype('scalar')->end() ->end() - ->scalarNode('trusted_proxies') + ->variableNode('trusted_proxies') ->beforeNormalization() - ->ifTrue(fn ($v) => 'private_ranges' === $v) - ->then(fn ($v) => implode(',', IpUtils::PRIVATE_SUBNETS)) + ->ifTrue(fn ($v) => 'private_ranges' === $v || 'PRIVATE_SUBNETS' === $v) + ->then(fn () => IpUtils::PRIVATE_SUBNETS) ->end() ->end() ->arrayNode('trusted_headers') diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 4b7431fc557f9..a6da61ae37ffb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -2354,7 +2354,7 @@ public function testTrustedProxiesWithPrivateRanges() { $container = $this->createContainerFromFile('trusted_proxies_private_ranges'); - $this->assertSame(IpUtils::PRIVATE_SUBNETS, array_map('trim', explode(',', $container->getParameter('kernel.trusted_proxies')))); + $this->assertSame(IpUtils::PRIVATE_SUBNETS, $container->getParameter('kernel.trusted_proxies')); } public function testWebhook() diff --git a/src/Symfony/Component/HttpFoundation/CHANGELOG.md b/src/Symfony/Component/HttpFoundation/CHANGELOG.md index ce1b339392bf0..c3814fddd62b7 100644 --- a/src/Symfony/Component/HttpFoundation/CHANGELOG.md +++ b/src/Symfony/Component/HttpFoundation/CHANGELOG.md @@ -6,6 +6,7 @@ CHANGELOG * Add optional `$requests` parameter to `RequestStack::__construct()` * Add optional `$v4Bytes` and `$v6Bytes` parameters to `IpUtils::anonymize()` + * Add `PRIVATE_SUBNETS` as a shortcut for private IP address ranges to `Request::setTrustedProxies()` 7.1 --- diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index c17d1d10c39fe..6c670cb08dee5 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -520,20 +520,26 @@ public function overrideGlobals(): void * * You should only list the reverse proxies that you manage directly. * - * @param array $proxies A list of trusted proxies, the string 'REMOTE_ADDR' will be replaced with $_SERVER['REMOTE_ADDR'] - * @param int $trustedHeaderSet A bit field of Request::HEADER_*, to set which headers to trust from your proxies + * @param array $proxies A list of trusted proxies, the string 'REMOTE_ADDR' will be replaced with $_SERVER['REMOTE_ADDR'] and 'PRIVATE_SUBNETS' by IpUtils::PRIVATE_SUBNETS + * @param int-mask-of $trustedHeaderSet A bit field to set which headers to trust from your proxies */ public static function setTrustedProxies(array $proxies, int $trustedHeaderSet): void { - self::$trustedProxies = array_reduce($proxies, function ($proxies, $proxy) { - if ('REMOTE_ADDR' !== $proxy) { - $proxies[] = $proxy; - } elseif (isset($_SERVER['REMOTE_ADDR'])) { - $proxies[] = $_SERVER['REMOTE_ADDR']; + if (false !== $i = array_search('REMOTE_ADDR', $proxies, true)) { + if (isset($_SERVER['REMOTE_ADDR'])) { + $proxies[$i] = $_SERVER['REMOTE_ADDR']; + } else { + unset($proxies[$i]); + $proxies = array_values($proxies); } + } + + if (false !== ($i = array_search('PRIVATE_SUBNETS', $proxies, true)) || false !== ($i = array_search('private_ranges', $proxies, true))) { + unset($proxies[$i]); + $proxies = array_merge($proxies, IpUtils::PRIVATE_SUBNETS); + } - return $proxies; - }, []); + self::$trustedProxies = $proxies; self::$trustedHeaderSet = $trustedHeaderSet; } diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index 6f35433220243..e23a88ddea848 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -16,6 +16,7 @@ use Symfony\Component\HttpFoundation\Exception\JsonException; use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException; use Symfony\Component\HttpFoundation\InputBag; +use Symfony\Component\HttpFoundation\IpUtils; use Symfony\Component\HttpFoundation\ParameterBag; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Session\Session; @@ -2564,6 +2565,26 @@ public function testTrustedProxiesRemoteAddr($serverRemoteAddr, $trustedProxies, $this->assertSame($result, Request::getTrustedProxies()); } + public static function trustedProxiesRemoteAddr() + { + return [ + ['1.1.1.1', ['REMOTE_ADDR'], ['1.1.1.1']], + ['1.1.1.1', ['REMOTE_ADDR', '2.2.2.2'], ['1.1.1.1', '2.2.2.2']], + [null, ['REMOTE_ADDR'], []], + [null, ['REMOTE_ADDR', '2.2.2.2'], ['2.2.2.2']], + ]; + } + + /** + * @testWith ["PRIVATE_SUBNETS"] + * ["private_ranges"] + */ + public function testTrustedProxiesPrivateSubnets(string $key) + { + Request::setTrustedProxies([$key], Request::HEADER_X_FORWARDED_FOR); + $this->assertSame(IpUtils::PRIVATE_SUBNETS, Request::getTrustedProxies()); + } + public function testTrustedValuesCache() { $request = Request::create('http://example.com/'); @@ -2581,16 +2602,6 @@ public function testTrustedValuesCache() $this->assertFalse($request->isSecure()); } - public static function trustedProxiesRemoteAddr() - { - return [ - ['1.1.1.1', ['REMOTE_ADDR'], ['1.1.1.1']], - ['1.1.1.1', ['REMOTE_ADDR', '2.2.2.2'], ['1.1.1.1', '2.2.2.2']], - [null, ['REMOTE_ADDR'], []], - [null, ['REMOTE_ADDR', '2.2.2.2'], ['2.2.2.2']], - ]; - } - /** * @dataProvider preferSafeContentData */