From 13f4b8962c987b8cae11059bcf43a674625b6ea9 Mon Sep 17 00:00:00 2001 From: Tony Cosentino Date: Fri, 11 Jul 2014 19:10:41 +0200 Subject: [PATCH 1/3] Added new Forwarded header support for Request::getClientIps --- .../Component/HttpFoundation/Request.php | 37 +++++++++------- .../HttpFoundation/Tests/RequestTest.php | 42 +++++++++++++++++++ 2 files changed, 64 insertions(+), 15 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index e809db04c9a41..788925cad8355 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -46,6 +46,9 @@ class Request const METHOD_TRACE = 'TRACE'; const METHOD_CONNECT = 'CONNECT'; + /** + * @var string[] + */ protected static $trustedProxies = array(); /** @@ -823,30 +826,34 @@ public function setSession(SessionInterface $session) */ public function getClientIps() { + $clientIps = null; $ip = $this->server->get('REMOTE_ADDR'); if (!self::$trustedProxies) { return array($ip); } - if (!self::$trustedHeaders[self::HEADER_CLIENT_IP] || !$this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) { - return array($ip); + if ($this->headers->has('Forwarded')) { + $forwardedHeader = $this->headers->get('Forwarded'); + preg_match_all('{(for)=("?\[?)([a-z0-9\.:_\-/]*)}', $forwardedHeader, $matches); + $clientIps = $matches[3]; + } elseif ($this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) { + $clientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP]))); } - $clientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP]))); - $clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from - - $ip = $clientIps[0]; // Fallback to this when the client IP falls into the range of trusted proxies + if ($clientIps) { + $clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from + $ip = $clientIps[0]; // Fallback to this when the client IP falls into the range of trusted proxies - // Eliminate all IPs from the forwarded IP chain which are trusted proxies - foreach ($clientIps as $key => $clientIp) { - // Remove port on IPv4 address (unfortunately, it does happen) - if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) { - $clientIps[$key] = $clientIp = $match[1]; - } - - if (IpUtils::checkIp($clientIp, self::$trustedProxies)) { - unset($clientIps[$key]); + // Eliminate all IPs from the forwarded IP chain which are trusted proxies + foreach ($clientIps as $key => $clientIp) { + // Remove port on IPv4 address (unfortunately, it does happen) + if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) { + $clientIps[$key] = $clientIp = $match[1]; + } + if (IpUtils::checkIp($clientIp, self::$trustedProxies)) { + unset($clientIps[$key]); + } } } diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index ef2262652c26d..5502e0246c65a 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -875,6 +875,29 @@ public function testGetClientIps($expected, $remoteAddr, $httpForwardedFor, $tru Request::setTrustedProxies(array()); } + /** + * @dataProvider testGetClientIpsForwardedProvider + */ + public function testGetClientIpsForwarded($expected, $remoteAddr, $httpForwarded, $trustedProxies) + { + $request = $this->getRequestInstanceForClientIpsForwardedTests($remoteAddr, $httpForwarded, $trustedProxies); + + $this->assertEquals($expected, $request->getClientIps()); + + Request::setTrustedProxies(array()); + } + + public function testGetClientIpsForwardedProvider() + { + // $expected $remoteAddr $httpForwarded $trustedProxies + return array( + array(array('127.0.0.1'), '127.0.0.1', 'for="_gazonk"', null), + array(array('_gazonk'), '127.0.0.1', 'for="_gazonk"', array('127.0.0.1')), + array(array('192.0.2.60'), '::1', 'for=192.0.2.60;proto=http;by=203.0.113.43', array('::1')), + array(array('2620:0:1cfe:face:b00c::3', '192.0.2.43'), '::1', 'for=192.0.2.43, for=2620:0:1cfe:face:b00c::3', array('::1')), + ); + } + public function testGetClientIpsProvider() { // $expected $remoteAddr $httpForwardedFor $trustedProxies @@ -1467,6 +1490,25 @@ private function getRequestInstanceForClientIpTests($remoteAddr, $httpForwardedF return $request; } + private function getRequestInstanceForClientIpsForwardedTests($remoteAddr, $httpForwarded, $trustedProxies) + { + $request = new Request(); + + $server = array('REMOTE_ADDR' => $remoteAddr); + + if (null !== $httpForwarded) { + $server['HTTP_FORWARDED'] = $httpForwarded; + } + + if ($trustedProxies) { + Request::setTrustedProxies($trustedProxies); + } + + $request->initialize(array(), array(), array(), array(), array(), $server); + + return $request; + } + public function testTrustedProxies() { $request = Request::create('http://example.com/'); From 7834df3a36c1256b7e8d3746b851552ff642a9d3 Mon Sep 17 00:00:00 2001 From: Tony Cosentino Date: Tue, 5 Aug 2014 19:56:38 +0200 Subject: [PATCH 2/3] Make sure the header is trusted --- src/Symfony/Component/HttpFoundation/Request.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 788925cad8355..e14801aa66684 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -30,6 +30,7 @@ */ class Request { + const HEADER_FORWARDED = 'forwarded'; const HEADER_CLIENT_IP = 'client_ip'; const HEADER_CLIENT_HOST = 'client_host'; const HEADER_CLIENT_PROTO = 'client_proto'; @@ -65,10 +66,13 @@ class Request * Names for headers that can be trusted when * using trusted proxies. * - * The default names are non-standard, but widely used + * The FORWARDED header is the standard as of rfc7239. + * + * The other headers are non-standard, but widely used * by popular reverse proxies (like Apache mod_proxy or Amazon EC2). */ protected static $trustedHeaders = array( + self::HEADER_FORWARDED => 'FORWARDED', self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR', self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST', self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO', @@ -833,11 +837,11 @@ public function getClientIps() return array($ip); } - if ($this->headers->has('Forwarded')) { + if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) { $forwardedHeader = $this->headers->get('Forwarded'); preg_match_all('{(for)=("?\[?)([a-z0-9\.:_\-/]*)}', $forwardedHeader, $matches); $clientIps = $matches[3]; - } elseif ($this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) { + } elseif (self::$trustedHeaders[self::HEADER_CLIENT_IP] && $this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) { $clientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP]))); } From dd1a560424a3cd3da3ad410f122543d71823640f Mon Sep 17 00:00:00 2001 From: Tony Cosentino Date: Wed, 1 Oct 2014 20:46:30 +0200 Subject: [PATCH 3/3] Updated after review --- .../Component/HttpFoundation/Request.php | 26 +++++++++---------- .../HttpFoundation/Tests/RequestTest.php | 2 ++ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index e14801aa66684..85d7ffa65aae3 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -830,7 +830,7 @@ public function setSession(SessionInterface $session) */ public function getClientIps() { - $clientIps = null; + $clientIps = array(); $ip = $this->server->get('REMOTE_ADDR'); if (!self::$trustedProxies) { @@ -838,26 +838,24 @@ public function getClientIps() } if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) { - $forwardedHeader = $this->headers->get('Forwarded'); + $forwardedHeader = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]); preg_match_all('{(for)=("?\[?)([a-z0-9\.:_\-/]*)}', $forwardedHeader, $matches); $clientIps = $matches[3]; } elseif (self::$trustedHeaders[self::HEADER_CLIENT_IP] && $this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) { $clientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP]))); } - if ($clientIps) { - $clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from - $ip = $clientIps[0]; // Fallback to this when the client IP falls into the range of trusted proxies + $clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from + $ip = $clientIps[0]; // Fallback to this when the client IP falls into the range of trusted proxies - // Eliminate all IPs from the forwarded IP chain which are trusted proxies - foreach ($clientIps as $key => $clientIp) { - // Remove port on IPv4 address (unfortunately, it does happen) - if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) { - $clientIps[$key] = $clientIp = $match[1]; - } - if (IpUtils::checkIp($clientIp, self::$trustedProxies)) { - unset($clientIps[$key]); - } + foreach ($clientIps as $key => $clientIp) { + // Remove port (unfortunately, it does happen) + if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) { + $clientIps[$key] = $clientIp = $match[1]; + } + + if (IpUtils::checkIp($clientIp, self::$trustedProxies)) { + unset($clientIps[$key]); } } diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index 5502e0246c65a..c052adb82fbe3 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -893,8 +893,10 @@ public function testGetClientIpsForwardedProvider() return array( array(array('127.0.0.1'), '127.0.0.1', 'for="_gazonk"', null), array(array('_gazonk'), '127.0.0.1', 'for="_gazonk"', array('127.0.0.1')), + array(array('88.88.88.88'), '127.0.0.1', 'for="88.88.88.88:80"', array('127.0.0.1')), array(array('192.0.2.60'), '::1', 'for=192.0.2.60;proto=http;by=203.0.113.43', array('::1')), array(array('2620:0:1cfe:face:b00c::3', '192.0.2.43'), '::1', 'for=192.0.2.43, for=2620:0:1cfe:face:b00c::3', array('::1')), + array(array('2001:db8:cafe::17'), '::1', 'for="[2001:db8:cafe::17]:4711', array('::1')), ); } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy