feature #15879 Deprecate the SecureRandom class (pierredup)
This PR was merged into the 2.8 branch.
Discussion
----------
Deprecate the SecureRandom class
| Q | A
| ------------- | ---
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | yes
| Tests pass? | yes
| Fixed tickets |
| License | MIT
| Doc PR |
Deprecate the `Symfony\Component\Security\Core\Util\SecureRandom` and encourage the usage of the `random_bytes` function instead
Commits
-------
f02973a
Deprecate the SecureRandom class
This commit is contained in:
commit
141f9eb46d
@ -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",
|
||||
|
@ -11,9 +11,7 @@
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
<service id="security.csrf.token_generator" class="%security.csrf.token_generator.class%" public="false">
|
||||
<argument type="service" id="security.secure_random" />
|
||||
</service>
|
||||
<service id="security.csrf.token_generator" class="%security.csrf.token_generator.class%" public="false" />
|
||||
|
||||
<service id="security.csrf.token_storage" class="%security.csrf.token_storage.class%" public="false">
|
||||
<argument type="service" id="session" />
|
||||
|
@ -164,6 +164,6 @@ EOF
|
||||
|
||||
private function generateSalt()
|
||||
{
|
||||
return base64_encode($this->getContainer()->get('security.secure_random')->nextBytes(30));
|
||||
return base64_encode(random_bytes(30));
|
||||
}
|
||||
}
|
||||
|
@ -45,9 +45,7 @@
|
||||
<service id="security.authentication.rememberme.services.persistent"
|
||||
class="%security.authentication.rememberme.services.persistent.class%"
|
||||
parent="security.authentication.rememberme.services.abstract"
|
||||
abstract="true">
|
||||
<argument type="service" id="security.secure_random" />
|
||||
</service>
|
||||
abstract="true" />
|
||||
|
||||
<service id="security.authentication.rememberme.services.simplehash"
|
||||
class="%security.authentication.rememberme.services.simplehash.class%"
|
||||
|
@ -12,6 +12,7 @@ CHANGELOG
|
||||
`Symfony\Component\Security\Http\Authentication\SimpleFormAuthenticatorInterface` instead
|
||||
* deprecated `Symfony\Component\Security\Core\Util\ClassUtils`, use
|
||||
`Symfony\Component\Security\Acl\Util\ClassUtils` instead
|
||||
* deprecated the `Symfony\Component\Security\Core\Util\SecureRandom` class in favor of the `random_bytes()` function
|
||||
* deprecated `supportsAttribute()` and `supportsClass()` methods of
|
||||
`Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface` and
|
||||
`Symfony\Component\Security\Core\Authorization\Voter\VoterInterface`.
|
||||
|
@ -13,26 +13,27 @@ namespace Symfony\Component\Security\Core\Tests\Util;
|
||||
|
||||
use Symfony\Component\Security\Core\Util\SecureRandom;
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
class SecureRandomTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* T1: Monobit test.
|
||||
*
|
||||
* @dataProvider getSecureRandoms
|
||||
*/
|
||||
public function testMonobit($secureRandom)
|
||||
public function testMonobit()
|
||||
{
|
||||
$secureRandom = new SecureRandom();
|
||||
$nbOnBits = substr_count($this->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 @@ class SecureRandomTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
/**
|
||||
* 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 @@ class SecureRandomTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
/**
|
||||
* 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 @@ class SecureRandomTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
/**
|
||||
* 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 @@ class SecureRandomTest extends \PHPUnit_Framework_TestCase
|
||||
$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 = '';
|
||||
|
@ -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 <fabien@symfony.com>
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,8 @@ namespace Symfony\Component\Security\Core\Util;
|
||||
* Interface that needs to be implemented by all secure random number generators.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @deprecated since version 2.8, to be removed in 3.0. Use the random_bytes function instead
|
||||
*/
|
||||
interface SecureRandomInterface
|
||||
{
|
||||
|
@ -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",
|
||||
|
@ -44,8 +44,7 @@ class UriSafeTokenGeneratorTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
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 @@ class UriSafeTokenGeneratorTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
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');
|
||||
|
@ -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 @@ use Symfony\Component\Security\Core\Util\SecureRandom;
|
||||
*/
|
||||
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 @@ class UriSafeTokenGenerator implements TokenGeneratorInterface
|
||||
// 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), '+/', '-_'), '=');
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -32,11 +32,12 @@ use Psr\Log\LoggerInterface;
|
||||
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 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices
|
||||
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 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices
|
||||
*/
|
||||
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(
|
||||
|
@ -20,7 +20,6 @@ use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
||||
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 @@ class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_Test
|
||||
$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()
|
||||
|
@ -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",
|
||||
|
@ -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": {
|
||||
|
Reference in New Issue
Block a user