Skip to content

Commit

Permalink
Fix #18817: Use paragonie/random_compat for random bytes and int ge…
Browse files Browse the repository at this point in the history
…neration
  • Loading branch information
samdark committed Aug 9, 2021
1 parent 200e1ba commit 13f27e4
Show file tree
Hide file tree
Showing 12 changed files with 85 additions and 353 deletions.
3 changes: 2 additions & 1 deletion composer.json
Expand Up @@ -78,7 +78,8 @@
"bower-asset/jquery": "3.6.*@stable | 3.5.*@stable | 3.4.*@stable | 3.3.*@stable | 3.2.*@stable | 3.1.*@stable | 2.2.*@stable | 2.1.*@stable | 1.11.*@stable | 1.12.*@stable",
"bower-asset/inputmask": "~3.2.2 | ~3.3.5",
"bower-asset/punycode": "1.3.*",
"bower-asset/yii2-pjax": "~2.0.1"
"bower-asset/yii2-pjax": "~2.0.1",
"paragonie/random_compat": ">=1"
},
"require-dev": {
"cweagans/composer-patches": "^1.7",
Expand Down
112 changes: 56 additions & 56 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions framework/CHANGELOG.md
Expand Up @@ -4,6 +4,7 @@ Yii Framework 2 Change Log
2.0.43 under development
------------------------

- Enh #18817: Use `paragonie/random_compat` for random bytes and int generation (samdark)
- Bug #14663: Do not convert int to string if database type of a column is numeric (egorrishe)
- Bug #18650: Refactor `framework/assets/yii.activeForm.js` arrow function into traditional function for IE11 compatibility (marcovtwout)
- Bug #18749: Fix `yii\web\ErrorHandler::encodeHtml()` to support strings with invalid UTF symbols (vjik)
Expand Down
89 changes: 1 addition & 88 deletions framework/base/Security.php
Expand Up @@ -116,14 +116,6 @@ protected function shouldUseLibreSSL()
return $this->_useLibreSSL;
}

/**
* @return bool if operating system is Windows
*/
private function isWindows()
{
return DIRECTORY_SEPARATOR !== '/';
}

/**
* Encrypts data using a password.
* Derives keys for encryption and authentication from the password using PBKDF2 and a random salt,
Expand Down Expand Up @@ -471,8 +463,6 @@ public function validateData($data, $key, $rawHash = false)
return false;
}

private $_randomFile;

/**
* Generates specified number of random bytes.
* Note that output may not be ASCII.
Expand All @@ -493,84 +483,7 @@ public function generateRandomKey($length = 32)
throw new InvalidArgumentException('First parameter ($length) must be greater than 0');
}

// always use random_bytes() if it is available
if (function_exists('random_bytes')) {
return random_bytes($length);
}

// The recent LibreSSL RNGs are faster and likely better than /dev/urandom.
// Since 5.4.0, openssl_random_pseudo_bytes() reads from CryptGenRandom on Windows instead
// of using OpenSSL library. LibreSSL is OK everywhere but don't use OpenSSL on non-Windows.
if (function_exists('openssl_random_pseudo_bytes')
&& ($this->shouldUseLibreSSL() || $this->isWindows())
) {
$key = openssl_random_pseudo_bytes($length, $cryptoStrong);
if ($cryptoStrong === false) {
throw new Exception(
'openssl_random_pseudo_bytes() set $crypto_strong false. Your PHP setup is insecure.'
);
}
if ($key !== false && StringHelper::byteLength($key) === $length) {
return $key;
}
}

// mcrypt_create_iv() does not use libmcrypt. Since PHP 5.3.7 it directly reads
// CryptGenRandom on Windows. Elsewhere it directly reads /dev/urandom.
if (function_exists('mcrypt_create_iv')) {
$key = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
if (StringHelper::byteLength($key) === $length) {
return $key;
}
}

// If not on Windows, try to open a random device.
if ($this->_randomFile === null && !$this->isWindows()) {
// urandom is a symlink to random on FreeBSD.
$device = PHP_OS === 'FreeBSD' ? '/dev/random' : '/dev/urandom';
// Check random device for special character device protection mode. Use lstat()
// instead of stat() in case an attacker arranges a symlink to a fake device.
$lstat = @lstat($device);
if ($lstat !== false && ($lstat['mode'] & 0170000) === 020000) {
$this->_randomFile = fopen($device, 'rb') ?: null;

if (is_resource($this->_randomFile)) {
// Reduce PHP stream buffer from default 8192 bytes to optimize data
// transfer from the random device for smaller values of $length.
// This also helps to keep future randoms out of user memory space.
$bufferSize = 8;

if (function_exists('stream_set_read_buffer')) {
stream_set_read_buffer($this->_randomFile, $bufferSize);
}
// stream_set_read_buffer() isn't implemented on HHVM
if (function_exists('stream_set_chunk_size')) {
stream_set_chunk_size($this->_randomFile, $bufferSize);
}
}
}
}

if (is_resource($this->_randomFile)) {
$buffer = '';
$stillNeed = $length;
while ($stillNeed > 0) {
$someBytes = fread($this->_randomFile, $stillNeed);
if ($someBytes === false) {
break;
}
$buffer .= $someBytes;
$stillNeed -= StringHelper::byteLength($someBytes);
if ($stillNeed === 0) {
// Leaving file pointer open in order to make next generation faster by reusing it.
return $buffer;
}
}
fclose($this->_randomFile);
$this->_randomFile = null;
}

throw new Exception('Unable to generate a random key');
return random_bytes($length);
}

/**
Expand Down
3 changes: 2 additions & 1 deletion framework/caching/DbCache.php
Expand Up @@ -276,7 +276,8 @@ protected function deleteValue($key)
*/
public function gc($force = false)
{
if ($force || mt_rand(0, 1000000) < $this->gcProbability) {

if ($force || random_int(0, 1000000) < $this->gcProbability) {
$this->db->createCommand()
->delete($this->cacheTable, '[[expire]] > 0 AND [[expire]] < ' . time())
->execute();
Expand Down
2 changes: 1 addition & 1 deletion framework/caching/FileCache.php
Expand Up @@ -245,7 +245,7 @@ protected function flushValues()
*/
public function gc($force = false, $expiredOnly = true)
{
if ($force || mt_rand(0, 1000000) < $this->gcProbability) {
if ($force || random_int(0, 1000000) < $this->gcProbability) {
$this->gcRecursive($this->cachePath, $expiredOnly);
}
}
Expand Down
17 changes: 9 additions & 8 deletions framework/captcha/CaptchaAction.php
Expand Up @@ -214,16 +214,17 @@ protected function generateVerifyCode()
if ($this->maxLength > 20) {
$this->maxLength = 20;
}
$length = mt_rand($this->minLength, $this->maxLength);

$length = random_int($this->minLength, $this->maxLength);

$letters = 'bcdfghjklmnpqrstvwxyz';
$vowels = 'aeiou';
$code = '';
for ($i = 0; $i < $length; ++$i) {
if ($i % 2 && mt_rand(0, 10) > 2 || !($i % 2) && mt_rand(0, 10) > 9) {
$code .= $vowels[mt_rand(0, 4)];
if ($i % 2 && random_int(0, 10) > 2 || !($i % 2) && random_int(0, 10) > 9) {
$code .= $vowels[random_int(0, 4)];
} else {
$code .= $letters[mt_rand(0, 20)];
$code .= $letters[random_int(0, 20)];
}
}

Expand Down Expand Up @@ -298,8 +299,8 @@ protected function renderImageByGD($code)
$x = 10;
$y = round($this->height * 27 / 40);
for ($i = 0; $i < $length; ++$i) {
$fontSize = (int) (mt_rand(26, 32) * $scale * 0.8);
$angle = mt_rand(-10, 10);
$fontSize = (int) (random_int(26, 32) * $scale * 0.8);
$angle = random_int(-10, 10);
$letter = $code[$i];
$box = imagettftext($image, $fontSize, $angle, $x, $y, $foreColor, $this->fontFile, $letter);
$x = $box[2] + $this->offset;
Expand Down Expand Up @@ -341,9 +342,9 @@ protected function renderImageByImagick($code)
for ($i = 0; $i < $length; ++$i) {
$draw = new \ImagickDraw();
$draw->setFont($this->fontFile);
$draw->setFontSize((int) (mt_rand(26, 32) * $scale * 0.8));
$draw->setFontSize((int) (random_int(26, 32) * $scale * 0.8));
$draw->setFillColor($foreColor);
$image->annotateImage($draw, $x, $y, mt_rand(-10, 10), $code[$i]);
$image->annotateImage($draw, $x, $y, random_int(-10, 10), $code[$i]);
$fontMetrics = $image->queryFontMetrics($draw, $code[$i]);
$x += (int) $fontMetrics['textWidth'] + $this->offset;
}
Expand Down
3 changes: 2 additions & 1 deletion framework/composer.json
Expand Up @@ -73,7 +73,8 @@
"bower-asset/jquery": "3.6.*@stable | 3.5.*@stable | 3.4.*@stable | 3.3.*@stable | 3.2.*@stable | 3.1.*@stable | 2.2.*@stable | 2.1.*@stable | 1.11.*@stable | 1.12.*@stable",
"bower-asset/inputmask": "~3.2.2 | ~3.3.5",
"bower-asset/punycode": "1.3.*",
"bower-asset/yii2-pjax": "~2.0.1"
"bower-asset/yii2-pjax": "~2.0.1",
"paragonie/random_compat": ">=1"
},
"autoload": {
"psr-4": {"yii\\": ""}
Expand Down
2 changes: 1 addition & 1 deletion framework/mail/BaseMailer.php
Expand Up @@ -343,7 +343,7 @@ public function generateMessageFileName()
{
$time = microtime(true);

return date('Ymd-His-', $time) . sprintf('%04d', (int) (($time - (int) $time) * 10000)) . '-' . sprintf('%04d', mt_rand(0, 10000)) . '.eml';
return date('Ymd-His-', $time) . sprintf('%04d', (int) (($time - (int) $time) * 10000)) . '-' . sprintf('%04d', random_int(0, 10000)) . '.eml';
}

/**
Expand Down

0 comments on commit 13f27e4

Please sign in to comment.