merged branch fabpot/trusted-proxies (PR #7735)
This PR was merged into the master branch. Discussion ---------- [HttpFoundation][FrameworkBundle] Add CIDR notation support in trusted proxy list | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #7312, #7262 | License | MIT | Doc PR | symfony/symfony-docs#2287 Should be rebased once #7734 is merged. Commits -------7b32794
[HttpFoundation] updated CHANGELOGe7c1696
[HttpFoundation] refactored code to avoid code duplication1695067
[HttpFoundation] added some unit tests for ranges of trusted IP addresses811434f
Allow setting trusted_proxies using CIDR notationddc9e38
Modify Request::getClientIp() to use IpUtils::checkIp()
This commit is contained in:
commit
5c53e6ba64
@ -46,7 +46,21 @@ class Configuration implements ConfigurationInterface
|
|||||||
->end()
|
->end()
|
||||||
->prototype('scalar')
|
->prototype('scalar')
|
||||||
->validate()
|
->validate()
|
||||||
->ifTrue(function($v) { return !empty($v) && !filter_var($v, FILTER_VALIDATE_IP); })
|
->ifTrue(function($v) {
|
||||||
|
if (empty($v)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false !== strpos($v, '/')) {
|
||||||
|
list($v, $mask) = explode('/', $v, 2);
|
||||||
|
|
||||||
|
if (strcmp($mask, (int) $mask) || $mask < 1 || $mask > (false !== strpos($v, ':') ? 128 : 32)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !filter_var($v, FILTER_VALIDATE_IP);
|
||||||
|
})
|
||||||
->thenInvalid('Invalid proxy IP "%s"')
|
->thenInvalid('Invalid proxy IP "%s"')
|
||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
|
@ -51,6 +51,8 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
|
|||||||
array(null, array()),
|
array(null, array()),
|
||||||
array(false, array()),
|
array(false, array()),
|
||||||
array(array(), array()),
|
array(array(), array()),
|
||||||
|
array(array('10.0.0.0/8'), array('10.0.0.0/8')),
|
||||||
|
array(array('::ffff:0:0/96'), array('::ffff:0:0/96')),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,9 @@ CHANGELOG
|
|||||||
=========
|
=========
|
||||||
|
|
||||||
2.3.0
|
2.3.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* added support for ranges of IPs in trusted proxies
|
||||||
* `UploadedFile::isValid` now returns false if the file was not uploaded via HTTP (in a non-test mode)
|
* `UploadedFile::isValid` now returns false if the file was not uploaded via HTTP (in a non-test mode)
|
||||||
|
|
||||||
2.2.0
|
2.2.0
|
||||||
|
@ -26,18 +26,26 @@ class IpUtils
|
|||||||
/**
|
/**
|
||||||
* Validates an IPv4 or IPv6 address.
|
* Validates an IPv4 or IPv6 address.
|
||||||
*
|
*
|
||||||
* @param string $requestIp
|
* @param string $requestIp
|
||||||
* @param string $ip
|
* @param string|array $ips
|
||||||
*
|
*
|
||||||
* @return boolean Whether the IP is valid
|
* @return boolean Whether the IP is valid
|
||||||
*/
|
*/
|
||||||
public static function checkIp($requestIp, $ip)
|
public static function checkIp($requestIp, $ips)
|
||||||
{
|
{
|
||||||
if (false !== strpos($requestIp, ':')) {
|
if (!is_array($ips)) {
|
||||||
return self::checkIp6($requestIp, $ip);
|
$ips = array($ips);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::checkIp4($requestIp, $ip);
|
$method = false !== strpos($requestIp, ':') ? 'checkIp6': 'checkIp4';
|
||||||
|
|
||||||
|
foreach ($ips as $ip) {
|
||||||
|
if (self::$method($requestIp, $ip)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -688,7 +688,14 @@ class Request
|
|||||||
|
|
||||||
$trustedProxies = !self::$trustedProxies ? array($ip) : self::$trustedProxies;
|
$trustedProxies = !self::$trustedProxies ? array($ip) : self::$trustedProxies;
|
||||||
$ip = $clientIps[0];
|
$ip = $clientIps[0];
|
||||||
$clientIps = array_values(array_diff($clientIps, $trustedProxies));
|
|
||||||
|
foreach ($clientIps as $key => $clientIp) {
|
||||||
|
if (IpUtils::checkIp($clientIp, $trustedProxies)) {
|
||||||
|
unset($clientIps[$key]);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $clientIps ? array_reverse($clientIps) : array($ip);
|
return $clientIps ? array_reverse($clientIps) : array($ip);
|
||||||
}
|
}
|
||||||
|
@ -153,10 +153,8 @@ class RequestMatcher implements RequestMatcherInterface
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->ips as $ip) {
|
if (IpUtils::checkIp($request->getClientIp(), $this->ips)) {
|
||||||
if (IpUtils::checkIp($request->getClientIp(), $ip)) {
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note to future implementors: add additional checks above the
|
// Note to future implementors: add additional checks above the
|
||||||
|
@ -31,6 +31,9 @@ class IpUtilsTest extends \PHPUnit_Framework_TestCase
|
|||||||
array(true, '192.168.1.1', '192.168.1.0/24'),
|
array(true, '192.168.1.1', '192.168.1.0/24'),
|
||||||
array(false, '192.168.1.1', '1.2.3.4/1'),
|
array(false, '192.168.1.1', '1.2.3.4/1'),
|
||||||
array(false, '192.168.1.1', '192.168.1/33'),
|
array(false, '192.168.1.1', '192.168.1/33'),
|
||||||
|
array(true, '192.168.1.1', array('1.2.3.4/1', '192.168.1.0/24')),
|
||||||
|
array(true, '192.168.1.1', array('192.168.1.0/24', '1.2.3.4/1')),
|
||||||
|
array(false, '192.168.1.1', array('1.2.3.4/1', '4.3.2.1/1')),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,6 +57,9 @@ class IpUtilsTest extends \PHPUnit_Framework_TestCase
|
|||||||
array(false, '2a01:198:603:0:396e:4789:8e99:890f', '::1'),
|
array(false, '2a01:198:603:0:396e:4789:8e99:890f', '::1'),
|
||||||
array(true, '0:0:0:0:0:0:0:1', '::1'),
|
array(true, '0:0:0:0:0:0:0:1', '::1'),
|
||||||
array(false, '0:0:603:0:396e:4789:8e99:0001', '::1'),
|
array(false, '0:0:603:0:396e:4789:8e99:0001', '::1'),
|
||||||
|
array(true, '2a01:198:603:0:396e:4789:8e99:890f', array('::1', '2a01:198:603:0::/65')),
|
||||||
|
array(true, '2a01:198:603:0:396e:4789:8e99:890f', array('2a01:198:603:0::/65', '::1')),
|
||||||
|
array(false, '2a01:198:603:0:396e:4789:8e99:890f', array('::1', '1a01:198:603:0::/65')),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -781,11 +781,15 @@ class RequestTest extends \PHPUnit_Framework_TestCase
|
|||||||
array(array('88.88.88.88'), '127.0.0.1', '88.88.88.88', array('127.0.0.1')),
|
array(array('88.88.88.88'), '127.0.0.1', '88.88.88.88', array('127.0.0.1')),
|
||||||
// forwarded for with remote IPv4 and all FF addrs trusted
|
// forwarded for with remote IPv4 and all FF addrs trusted
|
||||||
array(array('88.88.88.88'), '127.0.0.1', '88.88.88.88', array('127.0.0.1', '88.88.88.88')),
|
array(array('88.88.88.88'), '127.0.0.1', '88.88.88.88', array('127.0.0.1', '88.88.88.88')),
|
||||||
|
// forwarded for with remote IPv4 range trusted
|
||||||
|
array(array('88.88.88.88'), '123.45.67.89', '88.88.88.88', array('123.45.67.0/24')),
|
||||||
|
|
||||||
// forwarded for with remote IPv6 addr not trusted
|
// forwarded for with remote IPv6 addr not trusted
|
||||||
array(array('1620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '2620:0:1cfe:face:b00c::3', null),
|
array(array('1620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '2620:0:1cfe:face:b00c::3', null),
|
||||||
// forwarded for with remote IPv6 addr trusted
|
// forwarded for with remote IPv6 addr trusted
|
||||||
array(array('2620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '2620:0:1cfe:face:b00c::3', array('1620:0:1cfe:face:b00c::3')),
|
array(array('2620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '2620:0:1cfe:face:b00c::3', array('1620:0:1cfe:face:b00c::3')),
|
||||||
|
// forwarded for with remote IPv6 range trusted
|
||||||
|
array(array('88.88.88.88'), '2a01:198:603:0:396e:4789:8e99:890f', '88.88.88.88', array('2a01:198:603:0::/65')),
|
||||||
|
|
||||||
// multiple forwarded for with remote IPv4 addr trusted
|
// multiple forwarded for with remote IPv4 addr trusted
|
||||||
array(array('88.88.88.88', '87.65.43.21', '127.0.0.1'), '123.45.67.89', '127.0.0.1, 87.65.43.21, 88.88.88.88', array('123.45.67.89')),
|
array(array('88.88.88.88', '87.65.43.21', '127.0.0.1'), '123.45.67.89', '127.0.0.1, 87.65.43.21, 88.88.88.88', array('123.45.67.89')),
|
||||||
|
@ -80,10 +80,8 @@ class FragmentListener implements EventSubscriberInterface
|
|||||||
// does the Request come from a trusted IP?
|
// does the Request come from a trusted IP?
|
||||||
$trustedIps = array_merge($this->getLocalIpAddresses(), $request->getTrustedProxies());
|
$trustedIps = array_merge($this->getLocalIpAddresses(), $request->getTrustedProxies());
|
||||||
$remoteAddress = $request->server->get('REMOTE_ADDR');
|
$remoteAddress = $request->server->get('REMOTE_ADDR');
|
||||||
foreach ($trustedIps as $ip) {
|
if (IpUtils::checkIp($remoteAddress, $trustedIps)) {
|
||||||
if (IpUtils::checkIp($remoteAddress, $ip)) {
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// is the Request signed?
|
// is the Request signed?
|
||||||
|
Reference in New Issue
Block a user