diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index e809db04c9a41..85d7ffa65aae3 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'; @@ -46,6 +47,9 @@ class Request const METHOD_TRACE = 'TRACE'; const METHOD_CONNECT = 'CONNECT'; + /** + * @var string[] + */ protected static $trustedProxies = array(); /** @@ -62,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', @@ -823,24 +830,26 @@ public function setSession(SessionInterface $session) */ public function getClientIps() { + $clientIps = array(); $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 (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_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]))); } - $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 - // 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) + // Remove port (unfortunately, it does happen) if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) { $clientIps[$key] = $clientIp = $match[1]; } diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index ef2262652c26d..c052adb82fbe3 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -875,6 +875,31 @@ 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('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')), + ); + } + public function testGetClientIpsProvider() { // $expected $remoteAddr $httpForwardedFor $trustedProxies @@ -1467,6 +1492,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/'); 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