merged branch vicb/security (PR #5988)
This PR was merged into the 2.0 branch.
Commits
-------
e12bd12
[HttpFoundation] Make host & methods really case insensitive in the RequestMacther
Discussion
----------
[HttpFoundation] Make host & methods really case insensitive in the Requ...
...estMacther
and backport changes from 2.2
Details:
- does not take case into account when checking the host (the `Request` always returns a lowercase value) to protect against user typo,
- makes the constructor case proof by invoking setters rather than setting properties directly (you could then add un unreachable method i.e; `get`)
Please propagate to 2.1/2.2 if accpeted. Thanks.
This commit is contained in:
commit
d060fd4953
@ -20,19 +20,49 @@ namespace Symfony\Component\HttpFoundation;
|
||||
*/
|
||||
class RequestMatcher implements RequestMatcherInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
private $host;
|
||||
private $methods;
|
||||
private $ip;
|
||||
private $attributes;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $host;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $methods = array();
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $ip;
|
||||
|
||||
/**
|
||||
* Attributes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $attributes = array();
|
||||
|
||||
/**
|
||||
* @param string|null $path
|
||||
* @param string|null $host
|
||||
* @param string|string[]|null $methods
|
||||
* @param string|null $ip
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct($path = null, $host = null, $methods = null, $ip = null, array $attributes = array())
|
||||
{
|
||||
$this->path = $path;
|
||||
$this->host = $host;
|
||||
$this->methods = $methods;
|
||||
$this->ip = $ip;
|
||||
$this->attributes = $attributes;
|
||||
$this->matchPath($path);
|
||||
$this->matchHost($host);
|
||||
$this->matchMethod($methods);
|
||||
$this->matchIp($ip);
|
||||
foreach ($attributes as $k => $v) {
|
||||
$this->matchAttribute($k, $v);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -68,11 +98,11 @@ class RequestMatcher implements RequestMatcherInterface
|
||||
/**
|
||||
* Adds a check for the HTTP method.
|
||||
*
|
||||
* @param string|array $method An HTTP method or an array of HTTP methods
|
||||
* @param string|string[]|null $method An HTTP method or an array of HTTP methods
|
||||
*/
|
||||
public function matchMethod($method)
|
||||
{
|
||||
$this->methods = array_map('strtoupper', is_array($method) ? $method : array($method));
|
||||
$this->methods = array_map('strtoupper', (array) $method);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -93,7 +123,7 @@ class RequestMatcher implements RequestMatcherInterface
|
||||
*/
|
||||
public function matches(Request $request)
|
||||
{
|
||||
if (null !== $this->methods && !in_array($request->getMethod(), $this->methods)) {
|
||||
if ($this->methods && !in_array($request->getMethod(), $this->methods)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -111,7 +141,7 @@ class RequestMatcher implements RequestMatcherInterface
|
||||
}
|
||||
}
|
||||
|
||||
if (null !== $this->host && !preg_match('#'.str_replace('#', '\\#', $this->host).'#', $request->getHost())) {
|
||||
if (null !== $this->host && !preg_match('#'.str_replace('#', '\\#', $this->host).'#i', $request->getHost())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -122,6 +152,14 @@ class RequestMatcher implements RequestMatcherInterface
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an IP address.
|
||||
*
|
||||
* @param string $requestIp
|
||||
* @param string $ip
|
||||
*
|
||||
* @return boolean True valid, false if not.
|
||||
*/
|
||||
protected function checkIp($requestIp, $ip)
|
||||
{
|
||||
// IPv6 address
|
||||
@ -132,6 +170,14 @@ class RequestMatcher implements RequestMatcherInterface
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an IPv4 address.
|
||||
*
|
||||
* @param string $requestIp
|
||||
* @param string $ip
|
||||
*
|
||||
* @return boolean True valid, false if not.
|
||||
*/
|
||||
protected function checkIp4($requestIp, $ip)
|
||||
{
|
||||
if (false !== strpos($ip, '/')) {
|
||||
@ -149,8 +195,15 @@ class RequestMatcher implements RequestMatcherInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an IPv6 address.
|
||||
*
|
||||
* @author David Soria Parra <dsp at php dot net>
|
||||
* @see https://github.com/dsp/v6tools
|
||||
*
|
||||
* @param string $requestIp
|
||||
* @param string $ip
|
||||
*
|
||||
* @return boolean True valid, false if not.
|
||||
*/
|
||||
protected function checkIp6($requestIp, $ip)
|
||||
{
|
||||
@ -158,16 +211,25 @@ class RequestMatcher implements RequestMatcherInterface
|
||||
throw new \RuntimeException('Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".');
|
||||
}
|
||||
|
||||
list($address, $netmask) = explode('/', $ip, 2);
|
||||
if (false !== strpos($ip, '/')) {
|
||||
list($address, $netmask) = explode('/', $ip, 2);
|
||||
|
||||
$bytes_addr = unpack("n*", inet_pton($address));
|
||||
$bytes_test = unpack("n*", inet_pton($requestIp));
|
||||
if ($netmask < 1 || $netmask > 128) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$address = $ip;
|
||||
$netmask = 128;
|
||||
}
|
||||
|
||||
$bytesAddr = unpack("n*", inet_pton($address));
|
||||
$bytesTest = unpack("n*", inet_pton($requestIp));
|
||||
|
||||
for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; $i++) {
|
||||
$left = $netmask - 16 * ($i-1);
|
||||
$left = ($left <= 16) ? $left : 16;
|
||||
$mask = ~(0xffff >> $left) & 0xffff;
|
||||
if (($bytes_addr[$i] & $mask) != ($bytes_test[$i] & $mask)) {
|
||||
if (($bytesAddr[$i] & $mask) != ($bytesTest[$i] & $mask)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -175,3 +237,4 @@ class RequestMatcher implements RequestMatcherInterface
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,6 +66,9 @@ class RequestMatcherTest extends \PHPUnit_Framework_TestCase
|
||||
return array(
|
||||
array(true, '2a01:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'),
|
||||
array(false, '2a00:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'),
|
||||
array(false, '2a01:198:603:0:396e:4789:8e99:890f', '::1'),
|
||||
array(true, '0:0:0:0:0:0:0:1', '::1'),
|
||||
array(false, '0:0:603:0:396e:4789:8e99:0001', '::1'),
|
||||
);
|
||||
}
|
||||
|
||||
@ -89,38 +92,59 @@ class RequestMatcherTest extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
}
|
||||
|
||||
public function testMethod()
|
||||
/**
|
||||
* @dataProvider testMethodFixtures
|
||||
*/
|
||||
public function testMethod($requestMethod, $matcherMethod, $isMatch)
|
||||
{
|
||||
$matcher = new RequestMatcher();
|
||||
$matcher->matchMethod($matcherMethod);
|
||||
$request = Request::create('', $requestMethod);
|
||||
$this->assertSame($isMatch, $matcher->matches($request));
|
||||
|
||||
$matcher->matchMethod('get');
|
||||
$request = Request::create('', 'get');
|
||||
$this->assertTrue($matcher->matches($request));
|
||||
|
||||
$matcher->matchMethod('post');
|
||||
$this->assertFalse($matcher->matches($request));
|
||||
|
||||
$matcher->matchMethod(array('get', 'post'));
|
||||
$this->assertTrue($matcher->matches($request));
|
||||
$matcher = new RequestMatcher(null, null, $matcherMethod);
|
||||
$request = Request::create('', $requestMethod);
|
||||
$this->assertSame($isMatch, $matcher->matches($request));
|
||||
}
|
||||
|
||||
public function testHost()
|
||||
public function testMethodFixtures()
|
||||
{
|
||||
return array(
|
||||
array('get', 'get', true),
|
||||
array('get', array('get', 'post'), true),
|
||||
array('get', 'post', false),
|
||||
array('get', 'GET', true),
|
||||
array('get', array('GET', 'POST'), true),
|
||||
array('get', 'POST', false),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider testHostFixture
|
||||
*/
|
||||
public function testHost($pattern, $isMatch)
|
||||
{
|
||||
$matcher = new RequestMatcher();
|
||||
|
||||
$request = Request::create('', 'get', array(), array(), array(), array('HTTP_HOST' => 'foo.example.com'));
|
||||
|
||||
$matcher->matchHost('.*\.example\.com');
|
||||
$this->assertTrue($matcher->matches($request));
|
||||
$matcher->matchHost($pattern);
|
||||
$this->assertSame($isMatch, $matcher->matches($request));
|
||||
|
||||
$matcher->matchHost('\.example\.com$');
|
||||
$this->assertTrue($matcher->matches($request));
|
||||
$matcher= new RequestMatcher(null, $pattern);
|
||||
$this->assertSame($isMatch, $matcher->matches($request));
|
||||
}
|
||||
|
||||
$matcher->matchHost('^.*\.example\.com$');
|
||||
$this->assertTrue($matcher->matches($request));
|
||||
|
||||
$matcher->matchMethod('.*\.sensio\.com');
|
||||
$this->assertFalse($matcher->matches($request));
|
||||
public function testHostFixture()
|
||||
{
|
||||
return array(
|
||||
array('.*\.example\.com', true),
|
||||
array('\.example\.com$', true),
|
||||
array('^.*\.example\.com$', true),
|
||||
array('.*\.sensio\.com', false),
|
||||
array('.*\.example\.COM', true),
|
||||
array('\.example\.COM$', true),
|
||||
array('^.*\.example\.COM$', true),
|
||||
array('.*\.sensio\.COM', false), );
|
||||
}
|
||||
|
||||
public function testPath()
|
||||
@ -175,3 +199,4 @@ class RequestMatcherTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertFalse($matcher->matches($request));
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user