Package: release.debian.org Severity: normal Tags: wheezy User: release.debian....@packages.debian.org Usertags: pu
Hi, As per #806165 (Jessie pu request), this update aims to fix a security issue in zendframework: * Backport security fix from 1.12.17: - ZF2015-09: Fixed entropy issue in word CAPTCHA http://framework.zend.com/security/advisory/ZF2015-09 Thanks in advance for considering. Regards David
diff --git a/debian/changelog b/debian/changelog index 5e5e8cf..4b3947c 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +zendframework (1.11.13-1.1+deb7u5) wheezy; urgency=medium + + * Backport security fix from 1.12.17 + - ZF2015-09: Fixed entropy issue in word CAPTCHA + http://framework.zend.com/security/advisory/ZF2015-09 + + -- David Prévot <taf...@debian.org> Tue, 24 Nov 2015 18:28:53 -0400 + zendframework (1.11.13-1.1+deb7u4) wheezy-security; urgency=high * Backport security fixes from 1.12.16 diff --git a/debian/patches/0015-ZF2015-09-Fixed-entropy-issue-in-word-CAPTCHA.patch b/debian/patches/0015-ZF2015-09-Fixed-entropy-issue-in-word-CAPTCHA.patch new file mode 100644 index 0000000..718f86e --- /dev/null +++ b/debian/patches/0015-ZF2015-09-Fixed-entropy-issue-in-word-CAPTCHA.patch @@ -0,0 +1,337 @@ +From: Enrico Zimuel <e.zim...@gmail.com> +Date: Mon, 9 Nov 2015 17:26:45 +0100 +Subject: ZF2015-09: Fixed entropy issue in word CAPTCHA + +This patch fixes a potential entropy fixation vector with `Zend_Captcha_Word`. +Prior to the fix, when selecting letters for the CAPTCHA, `array_rand()` was +used, which does not use sufficient entropy during randomization. The patch +backports randomization routines from ZF2 in order to provide a more +cryptographically secure RNG. + +Origin: upstream, https://github.com/zendframework/zf1/commit/4a41392f89bf510a8ab801eacb117fe7ea25b575 +--- + library/Zend/Captcha/Word.php | 29 +++++++----- + library/Zend/Crypt/Math.php | 100 +++++++++++++++++++++++++++++++++++++++--- + tests/Zend/Crypt/MathTest.php | 72 +++++++++++++++++++++++++++++- + 3 files changed, 182 insertions(+), 19 deletions(-) + +diff --git a/library/Zend/Captcha/Word.php b/library/Zend/Captcha/Word.php +index 310cd2e..e0ddfe0 100644 +--- a/library/Zend/Captcha/Word.php ++++ b/library/Zend/Captcha/Word.php +@@ -22,6 +22,9 @@ + /** @see Zend_Captcha_Base */ + require_once 'Zend/Captcha/Base.php'; + ++/** @see Zend_Crypt_Math */ ++require_once 'Zend/Crypt/Math.php'; ++ + /** + * Word-based captcha adapter + * +@@ -39,10 +42,10 @@ abstract class Zend_Captcha_Word extends Zend_Captcha_Base + /**#@+ + * @var array Character sets + */ +- static $V = array("a", "e", "i", "o", "u", "y"); +- static $VN = array("a", "e", "i", "o", "u", "y","2","3","4","5","6","7","8","9"); +- static $C = array("b","c","d","f","g","h","j","k","m","n","p","q","r","s","t","u","v","w","x","z"); +- static $CN = array("b","c","d","f","g","h","j","k","m","n","p","q","r","s","t","u","v","w","x","z","2","3","4","5","6","7","8","9"); ++ static public $V = array("a", "e", "i", "o", "u", "y"); ++ static public $VN = array("a", "e", "i", "o", "u", "y","2","3","4","5","6","7","8","9"); ++ static public $C = array("b","c","d","f","g","h","j","k","m","n","p","q","r","s","t","u","v","w","x","z"); ++ static public $CN = array("b","c","d","f","g","h","j","k","m","n","p","q","r","s","t","u","v","w","x","z","2","3","4","5","6","7","8","9"); + /**#@-*/ + + /** +@@ -175,7 +178,7 @@ abstract class Zend_Captcha_Word extends Zend_Captcha_Base + * + * @return string + */ +- public function getId () ++ public function getId() + { + if (null === $this->_id) { + $this->_setId($this->_generateRandomId()); +@@ -189,7 +192,7 @@ abstract class Zend_Captcha_Word extends Zend_Captcha_Base + * @param string $id + * return Zend_Captcha_Word + */ +- protected function _setId ($id) ++ protected function _setId($id) + { + $this->_id = $id; + return $this; +@@ -250,7 +253,7 @@ abstract class Zend_Captcha_Word extends Zend_Captcha_Base + $this->_useNumbers = $_useNumbers; + return $this; + } +- ++ + /** + * Get session object + * +@@ -280,7 +283,7 @@ abstract class Zend_Captcha_Word extends Zend_Captcha_Base + public function setSession(Zend_Session_Namespace $session) + { + $this->_session = $session; +- if($session) { ++ if ($session) { + $this->_keepSession = true; + } + return $this; +@@ -326,10 +329,12 @@ abstract class Zend_Captcha_Word extends Zend_Captcha_Base + $vowels = $this->_useNumbers ? self::$VN : self::$V; + $consonants = $this->_useNumbers ? self::$CN : self::$C; + ++ $totIndexCon = count($consonants) - 1; ++ $totIndexVow = count($vowels) - 1; + for ($i=0; $i < $wordLen; $i = $i + 2) { + // generate word with mix of vowels and consonants +- $consonant = $consonants[array_rand($consonants)]; +- $vowel = $vowels[array_rand($vowels)]; ++ $consonant = $consonants[Zend_Crypt_Math::randInteger(0, $totIndexCon, true)]; ++ $vowel = $vowels[Zend_Crypt_Math::randInteger(0, $totIndexVow, true)]; + $word .= $consonant . $vowel; + } + +@@ -347,7 +352,7 @@ abstract class Zend_Captcha_Word extends Zend_Captcha_Base + */ + public function generate() + { +- if(!$this->_keepSession) { ++ if (!$this->_keepSession) { + $this->_session = null; + } + $id = $this->_generateRandomId(); +@@ -359,7 +364,7 @@ abstract class Zend_Captcha_Word extends Zend_Captcha_Base + + protected function _generateRandomId() + { +- return md5(mt_rand(0, 1000) . microtime(true)); ++ return md5(Zend_Crypt_Math::randBytes(32)); + } + + /** +diff --git a/library/Zend/Crypt/Math.php b/library/Zend/Crypt/Math.php +index 5b0ea9f..43f9167 100644 +--- a/library/Zend/Crypt/Math.php ++++ b/library/Zend/Crypt/Math.php +@@ -57,21 +57,109 @@ class Zend_Crypt_Math extends Zend_Crypt_Math_BigInteger + } + $rand = ''; + $i2 = strlen($maximum) - 1; +- for ($i = 1;$i < $i2;$i++) { +- $rand .= mt_rand(0,9); ++ for ($i = 1; $i < $i2; $i++) { ++ $rand .= mt_rand(0, 9); + } +- $rand .= mt_rand(0,9); ++ $rand .= mt_rand(0, 9); + return $rand; + } + + /** ++ * Return a random strings of $length bytes ++ * ++ * @param integer $length ++ * @param boolean $strong ++ * @return string ++ */ ++ public static function randBytes($length, $strong = false) ++ { ++ $length = (int) $length; ++ if ($length <= 0) { ++ return false; ++ } ++ if (function_exists('openssl_random_pseudo_bytes')) { ++ $bytes = openssl_random_pseudo_bytes($length, $usable); ++ if ($strong === $usable) { ++ return $bytes; ++ } ++ } ++ if (function_exists('mcrypt_create_iv')) { ++ $bytes = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM); ++ if ($bytes !== false && strlen($bytes) === $length) { ++ return $bytes; ++ } ++ } ++ if (file_exists('/dev/urandom') && is_readable('/dev/urandom')) { ++ $frandom = fopen('/dev/urandom', 'r'); ++ if ($frandom !== false) { ++ return fread($frandom, $length); ++ } ++ } ++ if (true === $strong) { ++ require_once 'Zend/Crypt/Exception.php'; ++ throw new Zend_Crypt_Exception( ++ 'This PHP environment doesn\'t support secure random number generation. ' . ++ 'Please consider installing the OpenSSL and/or Mcrypt extensions' ++ ); ++ } ++ $rand = ''; ++ for ($i = 0; $i < $length; $i++) { ++ $rand .= chr(mt_rand(0, 255)); ++ } ++ return $rand; ++ } ++ ++ /** ++ * Return a random integer between $min and $max ++ * ++ * @param integer $min ++ * @param integer $max ++ * @param boolean $strong ++ * @return integer ++ */ ++ public static function randInteger($min, $max, $strong = false) ++ { ++ if ($min > $max) { ++ require_once 'Zend/Crypt/Exception.php'; ++ throw new Zend_Crypt_Exception( ++ 'The min parameter must be lower than max parameter' ++ ); ++ } ++ $range = $max - $min; ++ if ($range == 0) { ++ return $max; ++ } elseif ($range > PHP_INT_MAX || is_float($range)) { ++ require_once 'Zend/Crypt/Exception.php'; ++ throw new Zend_Crypt_Exception( ++ 'The supplied range is too great to generate' ++ ); ++ } ++ // calculate number of bits required to store range on this machine ++ $r = $range; ++ $bits = 0; ++ while ($r) { ++ $bits++; ++ $r >>= 1; ++ } ++ $bits = (int) max($bits, 1); ++ $bytes = (int) max(ceil($bits / 8), 1); ++ $filter = (int) ((1 << $bits) - 1); ++ do { ++ $rnd = hexdec(bin2hex(self::randBytes($bytes, $strong))); ++ $rnd &= $filter; ++ } while ($rnd > $range); ++ return ($min + $rnd); ++ } ++ ++ /** + * Get the big endian two's complement of a given big integer in + * binary notation + * + * @param string $long + * @return string + */ +- public function btwoc($long) { ++ public function btwoc($long) ++ { + if (ord($long[0]) > 127) { + return "\x00" . $long; + } +@@ -84,7 +172,8 @@ class Zend_Crypt_Math extends Zend_Crypt_Math_BigInteger + * @param string $binary + * @return string + */ +- public function fromBinary($binary) { ++ public function fromBinary($binary) ++ { + return $this->_math->binaryToInteger($binary); + } + +@@ -98,5 +187,4 @@ class Zend_Crypt_Math extends Zend_Crypt_Math_BigInteger + { + return $this->_math->integerToBinary($integer); + } +- + } +diff --git a/tests/Zend/Crypt/MathTest.php b/tests/Zend/Crypt/MathTest.php +index d097186..f825cc9 100644 +--- a/tests/Zend/Crypt/MathTest.php ++++ b/tests/Zend/Crypt/MathTest.php +@@ -21,7 +21,7 @@ + */ + + require_once 'Zend/Crypt/Math.php'; +- ++require_once 'Zend/Crypt/Exception.php'; + + /** + * @category Zend +@@ -54,4 +54,74 @@ class Zend_Crypt_MathTest extends PHPUnit_Framework_TestCase + $this->assertTrue(bccomp($result, $lower) !== '-1'); + } + ++ public function testRandBytes() ++ { ++ for ($length = 1; $length < 4096; $length++) { ++ $rand = Zend_Crypt_Math::randBytes($length); ++ $this->assertTrue(false !== $rand); ++ $this->assertEquals($length, strlen($rand)); ++ } ++ } ++ ++ public function testRandInteger() ++ { ++ for ($i = 0; $i < 1024; $i++) { ++ $min = rand(1, PHP_INT_MAX/2); ++ $max = $min + rand(1, PHP_INT_MAX/2 - 1); ++ $rand = Zend_Crypt_Math::randInteger($min, $max); ++ $this->assertGreaterThanOrEqual($min, $rand); ++ $this->assertLessThanOrEqual($max, $rand); ++ } ++ } ++ ++ public static function provideRandInt() ++ { ++ return [ ++ [2, 1, 10000, 100, 0.9, 1.1, false], ++ [2, 1, 10000, 100, 0.8, 1.2, true] ++ ]; ++ } ++ ++ /** ++ * A Monte Carlo test that generates $cycles numbers from 0 to $tot ++ * and test if the numbers are above or below the line y=x with a ++ * frequency range of [$min, $max] ++ * ++ * @dataProvider provideRandInt ++ */ ++ public function testMontecarloRandInteger($num, $valid, $cycles, $tot, $min, $max, $strong) ++ { ++ try { ++ $test = Zend_Crypt_Math::randBytes(1, $strong); ++ } catch (Zend_Crypt_Exception $e) { ++ $this->markTestSkipped($e->getMessage()); ++ } ++ ++ $i = 0; ++ $count = 0; ++ do { ++ $up = 0; ++ $down = 0; ++ for ($i = 0; $i < $cycles; $i++) { ++ $x = Zend_Crypt_Math::randInteger(0, $tot, $strong); ++ $y = Zend_Crypt_Math::randInteger(0, $tot, $strong); ++ if ($x > $y) { ++ $up++; ++ } elseif ($x < $y) { ++ $down++; ++ } ++ } ++ $this->assertGreaterThan(0, $up); ++ $this->assertGreaterThan(0, $down); ++ $ratio = $up / $down; ++ if ($ratio > $min && $ratio < $max) { ++ $count++; ++ } ++ $i++; ++ } while ($i < $num && $count < $valid); ++ ++ if ($count < $valid) { ++ $this->fail('The random number generator failed the Monte Carlo test'); ++ } ++ } + } diff --git a/debian/patches/series b/debian/patches/series index dc960a0..8f3a09e 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -12,3 +12,4 @@ 0012-ZF2015-06-Fix-potential-XXE-vector-via-BOM-detection.patch 0013-ZF2015-07-Use-umask-of-0002.patch 0014-ZF2015-08-Fix-null-byte-injection-for-PDO-MsSql.patch +0015-ZF2015-09-Fixed-entropy-issue-in-word-CAPTCHA.patch
signature.asc
Description: PGP signature