use file_get_contents and not magic numbers

This commit is contained in:
Tom Worster
2015-12-20 11:48:36 -05:00
parent b8291b63cf
commit e7a888ad11
2 changed files with 46 additions and 46 deletions

View File

@ -30,6 +30,11 @@ use Yii;
*/ */
class Security extends Component class Security extends Component
{ {
const DEV_URANDOM = '/dev/urandom';
const SOURCE_MCRYPT = 'mcrypt';
const SOURCE_URANDOM = 'urandom';
const SOURCE_OPEN_SSL = 'OpenSSL';
const SOURCE_LIBRE_SSL = 'LibreSSL';
/** /**
* @var string The cipher to use for encryption and decryption. * @var string The cipher to use for encryption and decryption.
*/ */
@ -455,7 +460,7 @@ class Security extends Component
// The recent LibreSSL RNGs are faster and better than /dev/urandom. // The recent LibreSSL RNGs are faster and better than /dev/urandom.
// Parse OPENSSL_VERSION_TEXT because OPENSSL_VERSION_NUMBER is no use for LibreSSL. // Parse OPENSSL_VERSION_TEXT because OPENSSL_VERSION_NUMBER is no use for LibreSSL.
// https://bugs.php.net/bug.php?id=71143 // https://bugs.php.net/bug.php?id=71143
if ($this->_randomSource === 'LibreSSL' if ($this->_randomSource === self::SOURCE_LIBRE_SSL
|| ($this->_randomSource === null || ($this->_randomSource === null
&& defined('OPENSSL_VERSION_TEXT') && defined('OPENSSL_VERSION_TEXT')
&& preg_match('{^LibreSSL (\d\d?)\.(\d\d?)\.(\d\d?)$}', OPENSSL_VERSION_TEXT, $matches) && preg_match('{^LibreSSL (\d\d?)\.(\d\d?)\.(\d\d?)$}', OPENSSL_VERSION_TEXT, $matches)
@ -468,7 +473,7 @@ class Security extends Component
); );
} }
if ($key !== false && StringHelper::byteLength($key) === $length) { if ($key !== false && StringHelper::byteLength($key) === $length) {
$this->_randomSource = 'LibreSSL'; $this->_randomSource = self::SOURCE_LIBRE_SSL;
return $key; return $key;
} }
@ -478,14 +483,14 @@ class Security extends Component
// mcrypt_create_iv() does not use libmcrypt. Since PHP 5.3.7 it directly reads // mcrypt_create_iv() does not use libmcrypt. Since PHP 5.3.7 it directly reads
// CrypGenRandom on Windows. Elsewhere it directly reads /dev/urandom. // CrypGenRandom on Windows. Elsewhere it directly reads /dev/urandom.
if ($this->_randomSource === 'mcrypt' if ($this->_randomSource === self::SOURCE_MCRYPT
|| ($this->_randomSource === null || ($this->_randomSource === null
&& PHP_VERSION_ID >= 50307 && PHP_VERSION_ID >= 50307
&& function_exists('mcrypt_create_iv')) && function_exists('mcrypt_create_iv'))
) { ) {
$key = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM); $key = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
if (StringHelper::byteLength($key) === $length) { if (StringHelper::byteLength($key) === $length) {
$this->_randomSource = 'mcrypt'; $this->_randomSource = self::SOURCE_MCRYPT;
return $key; return $key;
} }
@ -493,48 +498,21 @@ class Security extends Component
$this->_randomSource = null; $this->_randomSource = null;
} }
// If not on Windows, try reading from /dev/urandom device. If successful, cache // If not on Windows, test for a /dev/urandom device.
// the file pointer in $this->_randomSource. if ($this->_randomSource === null && DIRECTORY_SEPARATOR === '/') {
if (is_resource($this->_randomSource) // Check it for speacial character device protection mode.
|| ($this->_randomSource === null $lstat = @lstat(self::DEV_URANDOM);
&& DIRECTORY_SEPARATOR === '/' $urandomDevice = $lstat !== false && ($lstat['mode'] & 0170000) === 020000;
&& @is_readable('/dev/urandom')) } else {
) { $urandomDevice = false;
// Either open /dev/urandom or get the cached file pointer. }
if (is_resource($this->_randomSource)) { if ($this->_randomSource === self::SOURCE_URANDOM || $urandomDevice) {
$urandomFile = $this->_randomSource; $key = @file_get_contents(self::DEV_URANDOM, false, null, 0, $length);
} else {
$urandomFile = fopen('/dev/urandom', 'rb');
if ($urandomFile) {
// Check the file's inode protection mode is 'character special'.
// NOTE: octal integer literals!
$fstat = fstat($urandomFile);
if (($fstat['mode'] & 0170000) !== 020000) {
fclose($urandomFile);
$urandomFile = null;
}
} else {
$urandomFile = null;
}
}
if ($urandomFile !== null) { if ($key !== false && StringHelper::byteLength($key) === $length) {
// $length could be large so read using a loop. $this->_randomSource = self::SOURCE_URANDOM;
$key = '';
do {
$buffer = fread($urandomFile, $length);
if (!$buffer) {
$key = null;
break;
}
$key .= $buffer;
} while (StringHelper::byteLength($key) < $length);
if ($key !== null && StringHelper::byteLength($key) === $length) { return $key;
$this->_randomSource = $urandomFile;
return $key;
}
} }
$this->_randomSource = null; $this->_randomSource = null;
@ -542,7 +520,7 @@ class Security extends Component
// Since 5.4.0, openssl_random_pseudo_bytes() reads from CryptGenRandom on Windows instead // Since 5.4.0, openssl_random_pseudo_bytes() reads from CryptGenRandom on Windows instead
// of using OpenSSL library. Don't use OpenSSL on other platforms. // of using OpenSSL library. Don't use OpenSSL on other platforms.
if ($this->_randomSource === 'OpenSSL' if ($this->_randomSource === self::SOURCE_OPEN_SSL
|| (DIRECTORY_SEPARATOR !== '/' && PHP_VERSION_ID >= 50400) || (DIRECTORY_SEPARATOR !== '/' && PHP_VERSION_ID >= 50400)
) { ) {
$key = openssl_random_pseudo_bytes($length, $cryptoStrong); $key = openssl_random_pseudo_bytes($length, $cryptoStrong);
@ -552,7 +530,7 @@ class Security extends Component
); );
} }
if ($key !== false && StringHelper::byteLength($key) === $length) { if ($key !== false && StringHelper::byteLength($key) === $length) {
$this->_randomSource = 'OpenSSL'; $this->_randomSource = self::SOURCE_OPEN_SSL;
return $key; return $key;
} }

View File

@ -810,6 +810,28 @@ TEXT;
$this->assertTrue($key1 != $key2); $this->assertTrue($key1 != $key2);
} }
public function testGenerateRandomKeyURandom()
{
if (function_exists('random_bytes')) {
$this->markTestSkipped('This test can only work on platforms where random_bytes() function does not exist. You may disable it in php.ini for testing. http://php.net/manual/en/ini.core.php#ini.disable-functions');
}
if (PHP_VERSION_ID >= 50307 && function_exists('mcrypt_create_iv')) {
$this->markTestSkipped('This test can only work on platforms where mcrypt_create_iv() function does not exist. You may disable it in php.ini for testing. http://php.net/manual/en/ini.core.php#ini.disable-functions');
}
if (!@is_readable('/dev/urandom')) {
$this->markTestSkipped('/dev/urandom does not seem to exist on your system.');
}
$length = 1024 * 1024;
$key1 = $this->security->generateRandomKey($length);
$this->assertInternalType('string', $key1);
$this->assertEquals($length, strlen($key1));
$key2 = $this->security->generateRandomKey($length);
$this->assertInternalType('string', $key2);
$this->assertEquals($length, strlen($key2));
$this->assertTrue($key1 != $key2);
}
public function testGenerateRandomString() public function testGenerateRandomString()
{ {
$length = 21; $length = 21;