diff --git a/composer.json b/composer.json index 3cfe44bcd9a9..32f87fd9eae7 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,8 @@ "doctrine/common": "~2.4", "twig/twig": "~1.20|~2.0", "psr/log": "~1.0", - "symfony/security-acl": "~2.7" + "symfony/security-acl": "~2.7", + "paragonie/random_compat": "~1.0" }, "replace": { "symfony/asset": "self.version", diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/security_csrf.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/security_csrf.xml index 143c8a68efe8..dc57fd61255d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/security_csrf.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/security_csrf.xml @@ -11,9 +11,7 @@ - - - + diff --git a/src/Symfony/Bundle/SecurityBundle/Command/UserPasswordEncoderCommand.php b/src/Symfony/Bundle/SecurityBundle/Command/UserPasswordEncoderCommand.php index 5dbd830a39da..0938b90db2c3 100644 --- a/src/Symfony/Bundle/SecurityBundle/Command/UserPasswordEncoderCommand.php +++ b/src/Symfony/Bundle/SecurityBundle/Command/UserPasswordEncoderCommand.php @@ -164,6 +164,6 @@ private function createPasswordQuestion() private function generateSalt() { - return base64_encode($this->getContainer()->get('security.secure_random')->nextBytes(30)); + return base64_encode(random_bytes(30)); } } diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml index b83c50bd96ef..1fdc9c221f2c 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml @@ -45,9 +45,7 @@ - - + abstract="true" /> getBitSequence($secureRandom, 20000), '1'); $this->assertTrue($nbOnBits > 9654 && $nbOnBits < 10346, 'Monobit test failed, number of turned on bits: '.$nbOnBits); } /** * T2: Chi-square test with 15 degrees of freedom (chi-Quadrat-Anpassungstest). - * - * @dataProvider getSecureRandoms */ - public function testPoker($secureRandom) + public function testPoker() { + $secureRandom = new SecureRandom(); $b = $this->getBitSequence($secureRandom, 20000); $c = array(); for ($i = 0; $i <= 15; ++$i) { @@ -56,11 +57,10 @@ public function testPoker($secureRandom) /** * Run test. - * - * @dataProvider getSecureRandoms */ - public function testRun($secureRandom) + public function testRun() { + $secureRandom = new SecureRandom(); $b = $this->getBitSequence($secureRandom, 20000); $runs = array(); @@ -104,11 +104,10 @@ public function testRun($secureRandom) /** * Long-run test. - * - * @dataProvider getSecureRandoms */ - public function testLongRun($secureRandom) + public function testLongRun() { + $secureRandom = new SecureRandom(); $b = $this->getBitSequence($secureRandom, 20000); $longestRun = $currentRun = 0; @@ -133,11 +132,10 @@ public function testLongRun($secureRandom) /** * Serial Correlation (Autokorrelationstest). - * - * @dataProvider getSecureRandoms */ - public function testSerialCorrelation($secureRandom) + public function testSerialCorrelation() { + $secureRandom = new SecureRandom(); $shift = mt_rand(1, 5000); $b = $this->getBitSequence($secureRandom, 20000); @@ -149,44 +147,6 @@ public function testSerialCorrelation($secureRandom) $this->assertTrue($Z > 2326 && $Z < 2674, 'Failed serial correlation test: '.$Z); } - public function getSecureRandoms() - { - $secureRandoms = array(); - - // only add if openssl is indeed present - $secureRandom = new SecureRandom(); - if ($this->hasOpenSsl($secureRandom)) { - $secureRandoms[] = array($secureRandom); - } - - // no-openssl with custom seed provider - $secureRandom = new SecureRandom(sys_get_temp_dir().'/_sf2.seed'); - $this->disableOpenSsl($secureRandom); - $secureRandoms[] = array($secureRandom); - - return $secureRandoms; - } - - protected function disableOpenSsl($secureRandom) - { - $ref = new \ReflectionProperty($secureRandom, 'useOpenSsl'); - $ref->setAccessible(true); - $ref->setValue($secureRandom, false); - $ref->setAccessible(false); - } - - protected function hasOpenSsl($secureRandom) - { - $ref = new \ReflectionProperty($secureRandom, 'useOpenSsl'); - $ref->setAccessible(true); - - $ret = $ref->getValue($secureRandom); - - $ref->setAccessible(false); - - return $ret; - } - private function getBitSequence($secureRandom, $length) { $bitSequence = ''; diff --git a/src/Symfony/Component/Security/Core/Util/SecureRandom.php b/src/Symfony/Component/Security/Core/Util/SecureRandom.php index 65722ce3ef77..06ed893ae771 100644 --- a/src/Symfony/Component/Security/Core/Util/SecureRandom.php +++ b/src/Symfony/Component/Security/Core/Util/SecureRandom.php @@ -11,106 +11,23 @@ namespace Symfony\Component\Security\Core\Util; -use Psr\Log\LoggerInterface; +@trigger_error('The '.__NAMESPACE__.'\SecureRandom class is deprecated since version 2.8 and will be removed in 3.0. Use the random_bytes() function instead.', E_USER_DEPRECATED); /** * A secure random number generator implementation. * * @author Fabien Potencier * @author Johannes M. Schmitt + * + * @deprecated since version 2.8, to be removed in 3.0. Use the random_bytes function instead */ final class SecureRandom implements SecureRandomInterface { - private $logger; - private $useOpenSsl; - private $seed; - private $seedUpdated; - private $seedLastUpdatedAt; - private $seedFile; - - /** - * Constructor. - * - * Be aware that a guessable seed will severely compromise the PRNG - * algorithm that is employed. - * - * @param string $seedFile - * @param LoggerInterface $logger - */ - public function __construct($seedFile = null, LoggerInterface $logger = null) - { - $this->seedFile = $seedFile; - $this->logger = $logger; - - // determine whether to use OpenSSL - if (!function_exists('random_bytes') && !function_exists('openssl_random_pseudo_bytes')) { - if (null !== $this->logger) { - $this->logger->notice('It is recommended that you install the "paragonie/random_compat" library or enable the "openssl" extension for random number generation.'); - } - $this->useOpenSsl = false; - } else { - $this->useOpenSsl = true; - } - } - /** * {@inheritdoc} */ public function nextBytes($nbBytes) { - if (function_exists('random_bytes')) { - return random_bytes($nbBytes); - } - - // try OpenSSL - if ($this->useOpenSsl) { - $bytes = openssl_random_pseudo_bytes($nbBytes, $strong); - - if (false !== $bytes && true === $strong) { - return $bytes; - } - - if (null !== $this->logger) { - $this->logger->info('OpenSSL did not produce a secure random number.'); - } - } - - // initialize seed - if (null === $this->seed) { - if (null === $this->seedFile) { - throw new \RuntimeException('You need to specify a file path to store the seed.'); - } - - if (is_file($this->seedFile)) { - list($this->seed, $this->seedLastUpdatedAt) = $this->readSeed(); - } else { - $this->seed = uniqid(mt_rand(), true); - $this->updateSeed(); - } - } - - $bytes = ''; - while (strlen($bytes) < $nbBytes) { - static $incr = 1; - $bytes .= hash('sha512', $incr++.$this->seed.uniqid(mt_rand(), true).$nbBytes, true); - $this->seed = base64_encode(hash('sha512', $this->seed.$bytes.$nbBytes, true)); - $this->updateSeed(); - } - - return substr($bytes, 0, $nbBytes); - } - - private function readSeed() - { - return json_decode(file_get_contents($this->seedFile)); - } - - private function updateSeed() - { - if (!$this->seedUpdated && $this->seedLastUpdatedAt < time() - mt_rand(1, 10)) { - file_put_contents($this->seedFile, json_encode(array($this->seed, microtime(true)))); - } - - $this->seedUpdated = true; + return random_bytes($nbBytes); } } diff --git a/src/Symfony/Component/Security/Core/Util/SecureRandomInterface.php b/src/Symfony/Component/Security/Core/Util/SecureRandomInterface.php index 87d3aceeebf3..df5509beb1c2 100644 --- a/src/Symfony/Component/Security/Core/Util/SecureRandomInterface.php +++ b/src/Symfony/Component/Security/Core/Util/SecureRandomInterface.php @@ -15,6 +15,8 @@ * Interface that needs to be implemented by all secure random number generators. * * @author Fabien Potencier + * + * @deprecated since version 2.8, to be removed in 3.0. Use the random_bytes function instead */ interface SecureRandomInterface { diff --git a/src/Symfony/Component/Security/Core/composer.json b/src/Symfony/Component/Security/Core/composer.json index 6a1ac993c9db..e4b90beb8467 100644 --- a/src/Symfony/Component/Security/Core/composer.json +++ b/src/Symfony/Component/Security/Core/composer.json @@ -16,7 +16,8 @@ } ], "require": { - "php": ">=5.3.9" + "php": ">=5.3.9", + "paragonie/random_compat" : "~1.0" }, "require-dev": { "symfony/phpunit-bridge": "~2.7|~3.0.0", diff --git a/src/Symfony/Component/Security/Csrf/Tests/TokenGenerator/UriSafeTokenGeneratorTest.php b/src/Symfony/Component/Security/Csrf/Tests/TokenGenerator/UriSafeTokenGeneratorTest.php index 1b325e5d1e8c..e4ea80c50788 100644 --- a/src/Symfony/Component/Security/Csrf/Tests/TokenGenerator/UriSafeTokenGeneratorTest.php +++ b/src/Symfony/Component/Security/Csrf/Tests/TokenGenerator/UriSafeTokenGeneratorTest.php @@ -44,8 +44,7 @@ public static function setUpBeforeClass() protected function setUp() { - $this->random = $this->getMock('Symfony\Component\Security\Core\Util\SecureRandomInterface'); - $this->generator = new UriSafeTokenGenerator($this->random, self::ENTROPY); + $this->generator = new UriSafeTokenGenerator(self::ENTROPY); } protected function tearDown() @@ -56,11 +55,6 @@ protected function tearDown() public function testGenerateToken() { - $this->random->expects($this->once()) - ->method('nextBytes') - ->with(self::ENTROPY / 8) - ->will($this->returnValue(self::$bytes)); - $token = $this->generator->generateToken(); $this->assertTrue(ctype_print($token), 'is printable'); diff --git a/src/Symfony/Component/Security/Csrf/TokenGenerator/UriSafeTokenGenerator.php b/src/Symfony/Component/Security/Csrf/TokenGenerator/UriSafeTokenGenerator.php index edeb435138f2..fa5a72245f25 100644 --- a/src/Symfony/Component/Security/Csrf/TokenGenerator/UriSafeTokenGenerator.php +++ b/src/Symfony/Component/Security/Csrf/TokenGenerator/UriSafeTokenGenerator.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Security\Csrf\TokenGenerator; use Symfony\Component\Security\Core\Util\SecureRandomInterface; -use Symfony\Component\Security\Core\Util\SecureRandom; /** * Generates CSRF tokens. @@ -23,13 +22,6 @@ */ class UriSafeTokenGenerator implements TokenGeneratorInterface { - /** - * The generator for random values. - * - * @var SecureRandomInterface - */ - private $random; - /** * The amount of entropy collected for each token (in bits). * @@ -40,15 +32,17 @@ class UriSafeTokenGenerator implements TokenGeneratorInterface /** * Generates URI-safe CSRF tokens. * - * @param SecureRandomInterface|null $random The random value generator used for - * generating entropy - * @param int $entropy The amount of entropy collected for - * each token (in bits) + * @param int $entropy The amount of entropy collected for each token (in bits) */ - public function __construct(SecureRandomInterface $random = null, $entropy = 256) + public function __construct($entropy = 256) { - $this->random = $random ?: new SecureRandom(); - $this->entropy = $entropy; + if ($entropy instanceof SecureRandomInterface || func_num_args() === 2) { + @trigger_error('The '.__METHOD__.' method now requires the entropy to be given as the first argument. The SecureRandomInterface will be removed in 3.0.', E_USER_DEPRECATED); + + $this->entropy = func_num_args() === 2 ? func_get_arg(1) : 256; + } else { + $this->entropy = $entropy; + } } /** @@ -59,7 +53,7 @@ public function generateToken() // Generate an URI safe base64 encoded string that does not contain "+", // "/" or "=" which need to be URL encoded and make URLs unnecessarily // longer. - $bytes = $this->random->nextBytes($this->entropy / 8); + $bytes = random_bytes($this->entropy / 8); return rtrim(strtr(base64_encode($bytes), '+/', '-_'), '='); } diff --git a/src/Symfony/Component/Security/Csrf/composer.json b/src/Symfony/Component/Security/Csrf/composer.json index 20fd2ffebd42..95b1c59cc68d 100644 --- a/src/Symfony/Component/Security/Csrf/composer.json +++ b/src/Symfony/Component/Security/Csrf/composer.json @@ -17,7 +17,8 @@ ], "require": { "php": ">=5.3.9", - "symfony/security-core": "~2.4|~3.0.0" + "symfony/security-core": "~2.4|~3.0.0", + "paragonie/random_compat" : "~1.0" }, "require-dev": { "symfony/phpunit-bridge": "~2.7|~3.0.0", diff --git a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php index 3e465d68704d..996eca358827 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php @@ -32,11 +32,12 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices { private $tokenProvider; - private $secureRandom; /** * Constructor. * + * Note: The $secureRandom parameter is deprecated since version 2.8 and will be removed in 3.0. + * * @param array $userProviders * @param string $secret * @param string $providerKey @@ -44,11 +45,13 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices * @param LoggerInterface $logger * @param SecureRandomInterface $secureRandom */ - public function __construct(array $userProviders, $secret, $providerKey, array $options = array(), LoggerInterface $logger = null, SecureRandomInterface $secureRandom) + public function __construct(array $userProviders, $secret, $providerKey, array $options = array(), LoggerInterface $logger = null, SecureRandomInterface $secureRandom = null) { - parent::__construct($userProviders, $secret, $providerKey, $options, $logger); + if (null !== $secureRandom) { + @trigger_error('The $secureRandom parameter in '.__METHOD__.' is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + } - $this->secureRandom = $secureRandom; + parent::__construct($userProviders, $secret, $providerKey, $options, $logger); } /** @@ -98,7 +101,7 @@ protected function processAutoLoginCookie(array $cookieParts, Request $request) throw new AuthenticationException('The cookie has expired.'); } - $tokenValue = base64_encode($this->secureRandom->nextBytes(64)); + $tokenValue = base64_encode(random_bytes(64)); $this->tokenProvider->updateToken($series, $tokenValue, new \DateTime()); $request->attributes->set(self::COOKIE_ATTR_NAME, new Cookie( @@ -120,8 +123,8 @@ protected function processAutoLoginCookie(array $cookieParts, Request $request) */ protected function onLoginSuccess(Request $request, Response $response, TokenInterface $token) { - $series = base64_encode($this->secureRandom->nextBytes(64)); - $tokenValue = base64_encode($this->secureRandom->nextBytes(64)); + $series = base64_encode(random_bytes(64)); + $tokenValue = base64_encode(random_bytes(64)); $this->tokenProvider->createNewToken( new PersistentToken( diff --git a/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php b/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php index 502d17077b98..581bdfa8b13f 100644 --- a/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php +++ b/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php @@ -20,7 +20,6 @@ use Symfony\Component\Security\Http\RememberMe\PersistentTokenBasedRememberMeServices; use Symfony\Component\Security\Core\Exception\TokenNotFoundException; use Symfony\Component\Security\Core\Exception\CookieTheftException; -use Symfony\Component\Security\Core\Util\SecureRandom; class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase { @@ -313,7 +312,7 @@ protected function getService($userProvider = null, $options = array(), $logger $userProvider = $this->getProvider(); } - return new PersistentTokenBasedRememberMeServices(array($userProvider), 'foosecret', 'fookey', $options, $logger, new SecureRandom(sys_get_temp_dir().'/_sf2.seed')); + return new PersistentTokenBasedRememberMeServices(array($userProvider), 'foosecret', 'fookey', $options, $logger); } protected function getProvider() diff --git a/src/Symfony/Component/Security/Http/composer.json b/src/Symfony/Component/Security/Http/composer.json index c4a4349acfbe..f7ecca03be27 100644 --- a/src/Symfony/Component/Security/Http/composer.json +++ b/src/Symfony/Component/Security/Http/composer.json @@ -21,7 +21,8 @@ "symfony/event-dispatcher": "~2.1|~3.0.0", "symfony/http-foundation": "~2.4|~3.0.0", "symfony/http-kernel": "~2.4|~3.0.0", - "symfony/property-access": "~2.3|~3.0.0" + "symfony/property-access": "~2.3|~3.0.0", + "paragonie/random_compat" : "~1.0" }, "require-dev": { "symfony/phpunit-bridge": "~2.7|~3.0.0", diff --git a/src/Symfony/Component/Security/composer.json b/src/Symfony/Component/Security/composer.json index 8a585207b365..63102b843e57 100644 --- a/src/Symfony/Component/Security/composer.json +++ b/src/Symfony/Component/Security/composer.json @@ -21,7 +21,8 @@ "symfony/event-dispatcher": "~2.2|~3.0.0", "symfony/http-foundation": "~2.1|~3.0.0", "symfony/http-kernel": "~2.4|~3.0.0", - "symfony/property-access": "~2.3|~3.0.0" + "symfony/property-access": "~2.3|~3.0.0", + "paragonie/random_compat" : "~1.0" }, "replace": { "symfony/security-core": "self.version", @@ -51,7 +52,6 @@ "symfony/routing": "For using the HttpUtils class to create sub-requests, redirect the user, and match URLs", "symfony/expression-language": "For using the expression voter", "ircmaxell/password-compat": "For using the BCrypt password encoder in PHP <5.5", - "paragonie/random_compat": "", "symfony/ldap": "For using the LDAP user and authentication providers" }, "autoload": {