From 464c39a77f21fae62898433393bbd497643822eb Mon Sep 17 00:00:00 2001 From: Dawid Nowak Date: Sun, 17 May 2015 22:37:53 +0200 Subject: [PATCH] [Security] AbstractRememberMeServices::encodeCookie() validates cookie parts --- .../RememberMe/AbstractRememberMeServices.php | 8 +++++ .../TokenBasedRememberMeServices.php | 4 --- .../AbstractRememberMeServicesTest.php | 34 +++++++++++++++++++ 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php index b14e36da4f..16f7831e7c 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php @@ -268,9 +268,17 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface * @param array $cookieParts * * @return string + * + * @throws \InvalidArgumentException When $cookieParts contain the cookie delimiter. Extending class should either remove or escape it. */ protected function encodeCookie(array $cookieParts) { + foreach ($cookieParts as $cookiePart) { + if (false !== strpos($cookiePart, self::COOKIE_DELIMITER)) { + throw new \InvalidArgumentException(sprintf('$cookieParts should not contain the cookie delimiter "%s"', self::COOKIE_DELIMITER)); + } + } + return base64_encode(implode(self::COOKIE_DELIMITER, $cookieParts)); } diff --git a/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php index 3d2cf12cb0..a129b1d984 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php @@ -119,8 +119,6 @@ class TokenBasedRememberMeServices extends AbstractRememberMeServices * @param int $expires The Unix timestamp when the cookie expires * @param string $password The encoded password * - * @throws \RuntimeException if username contains invalid chars - * * @return string */ protected function generateCookieValue($class, $username, $expires, $password) @@ -141,8 +139,6 @@ class TokenBasedRememberMeServices extends AbstractRememberMeServices * @param int $expires The Unix timestamp when the cookie expires * @param string $password The encoded password * - * @throws \RuntimeException when the private key is empty - * * @return string */ protected function generateCookieHash($class, $username, $expires, $password) diff --git a/src/Symfony/Component/Security/Tests/Http/RememberMe/AbstractRememberMeServicesTest.php b/src/Symfony/Component/Security/Tests/Http/RememberMe/AbstractRememberMeServicesTest.php index 0f64730b7b..70ff6a0df4 100644 --- a/src/Symfony/Component/Security/Tests/Http/RememberMe/AbstractRememberMeServicesTest.php +++ b/src/Symfony/Component/Security/Tests/Http/RememberMe/AbstractRememberMeServicesTest.php @@ -14,6 +14,7 @@ namespace Symfony\Component\Security\Tests\Http\RememberMe; use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Http\RememberMe\AbstractRememberMeServices; class AbstractRememberMeServicesTest extends \PHPUnit_Framework_TestCase { @@ -236,6 +237,30 @@ class AbstractRememberMeServicesTest extends \PHPUnit_Framework_TestCase ); } + public function testEncodeCookieAndDecodeCookieAreInvertible() + { + $cookieParts = array('aa', 'bb', 'cc'); + $service = $this->getService(); + + $encoded = $this->callProtected($service, 'encodeCookie', array($cookieParts)); + $this->assertInternalType('string', $encoded); + + $decoded = $this->callProtected($service, 'decodeCookie', array($encoded)); + $this->assertSame($cookieParts, $decoded); + } + + /** + * @expectedException InvalidArgumentException + * @expectedExceptionMessage cookie delimiter + */ + public function testThereShouldBeNoCookieDelimiterInCookieParts() + { + $cookieParts = array('aa', 'b'.AbstractRememberMeServices::COOKIE_DELIMITER.'b', 'cc'); + $service = $this->getService(); + + $this->callProtected($service, 'encodeCookie', array($cookieParts)); + } + protected function getService($userProvider = null, $options = array(), $logger = null) { if (null === $userProvider) { @@ -258,4 +283,13 @@ class AbstractRememberMeServicesTest extends \PHPUnit_Framework_TestCase return $provider; } + + private function callProtected($object, $method, array $args) + { + $reflection = new \ReflectionClass(get_class($object)); + $reflectionMethod = $reflection->getMethod($method); + $reflectionMethod->setAccessible(true); + + return $reflectionMethod->invokeArgs($object, $args); + } }