diff --git a/composer.json b/composer.json index dfaee3a6363..51342b2bcc0 100644 --- a/composer.json +++ b/composer.json @@ -18,11 +18,14 @@ "source": "https://github.com/cakephp/cakephp" }, "require": { - "php": ">=5.3.0", - "ext-mcrypt": "*" + "php": ">=5.3.0" + }, + "suggest": { + "ext-openssl": "You need to install ext-openssl or ext-mcrypt to use AES-256 encryption", + "ext-mcrypt": "You need to install ext-openssl or ext-mcrypt to use AES-256 encryption" }, "require-dev": { - "phpunit/phpunit": "<6.0.0", + "phpunit/phpunit": "^3.7", "cakephp/cakephp-codesniffer": "^1.0.0" }, "config": { diff --git a/lib/Cake/Model/Datasource/Database/Sqlserver.php b/lib/Cake/Model/Datasource/Database/Sqlserver.php index 2e9c78fd706..c25934409b4 100644 --- a/lib/Cake/Model/Datasource/Database/Sqlserver.php +++ b/lib/Cake/Model/Datasource/Database/Sqlserver.php @@ -302,6 +302,10 @@ public function fields(Model $model, $alias = null, $fields = array(), $quote = $prepend = 'DISTINCT '; $fields[$i] = trim(str_replace('DISTINCT', '', $fields[$i])); } + if (strpos($fields[$i], 'COUNT(DISTINCT') !== false) { + $prepend = 'COUNT(DISTINCT '; + $fields[$i] = trim(str_replace('COUNT(DISTINCT', '', $this->_quoteFields($fields[$i]))); + } if (!preg_match('/\s+AS\s+/i', $fields[$i])) { if (substr($fields[$i], -1) === '*') { diff --git a/lib/Cake/Model/Datasource/DboSource.php b/lib/Cake/Model/Datasource/DboSource.php index 9403708a91d..40f682b1777 100644 --- a/lib/Cake/Model/Datasource/DboSource.php +++ b/lib/Cake/Model/Datasource/DboSource.php @@ -470,7 +470,7 @@ protected function _execute($sql, $params = array(), $prepareOptions = array()) $query = $this->_connection->prepare($sql, $prepareOptions); $query->setFetchMode(PDO::FETCH_LAZY); if (!$query->execute($params)) { - $this->_results = $query; + $this->_result = $query; $query->closeCursor(); return false; } diff --git a/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php b/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php index 4013d215d6b..622992ab1d7 100644 --- a/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php @@ -383,6 +383,10 @@ public function testDistinctFields() { $result = $this->db->fields($this->model, null, 'DISTINCT Car.country_code'); $expected = array('DISTINCT [Car].[country_code] AS [Car__country_code]'); $this->assertEquals($expected, $result); + + $result = $this->db->fields($this->model, null, 'COUNT(DISTINCT Car.country_code)'); + $expected = array('COUNT(DISTINCT [Car].[country_code]) AS [Car__country_code]'); + $this->assertEquals($expected, $result); } /** diff --git a/lib/Cake/Test/Case/Utility/SecurityTest.php b/lib/Cake/Test/Case/Utility/SecurityTest.php index fc7c25bdda1..2b1fe1ab79b 100644 --- a/lib/Cake/Test/Case/Utility/SecurityTest.php +++ b/lib/Cake/Test/Case/Utility/SecurityTest.php @@ -29,6 +29,26 @@ class SecurityTest extends CakeTestCase { */ public $sut = null; +/** + * setUp method + * + * @return void + */ + public function setUp() { + parent::setUp(); + Configure::delete('Security.useOpenSsl'); + } + +/** + * tearDown method + * + * @return void + */ + public function tearDown() { + parent::tearDown(); + Configure::delete('Security.useOpenSsl'); + } + /** * testInactiveMins method * @@ -337,6 +357,54 @@ public function testEncryptDecrypt() { $this->assertEquals($txt, Security::decrypt($result, $key)); } +/** + * Tests that encrypted strings are comatible between the mcrypt and openssl engine. + * + * @dataProvider plainTextProvider + * @param string $txt Plain text to be encrypted. + * @return void + */ + public function testEncryptDecryptCompatibility($txt) { + $this->skipIf(!extension_loaded('mcrypt'), 'This test requires mcrypt to be installed'); + $this->skipIf(!extension_loaded('openssl'), 'This test requires openssl to be installed'); + $this->skipIf(version_compare(PHP_VERSION, '5.3.3', '<'), 'This test requires PHP 5.3.3 or greater'); + + $key = '12345678901234567890123456789012'; + + Configure::write('Security.useOpenSsl', false); + $mcrypt = Security::encrypt($txt, $key); + + Configure::write('Security.useOpenSsl', true); + $openssl = Security::encrypt($txt, $key); + + $this->assertEquals(strlen($mcrypt), strlen($openssl)); + + Configure::write('Security.useOpenSsl', false); + $this->assertEquals($txt, Security::decrypt($mcrypt, $key)); + $this->assertEquals($txt, Security::decrypt($openssl, $key)); + + Configure::write('Security.useOpenSsl', true); + $this->assertEquals($txt, Security::decrypt($mcrypt, $key)); + $this->assertEquals($txt, Security::decrypt($openssl, $key)); + } + +/** + * Data provider for testEncryptDecryptCompatibility + * + * @return array + */ + public function plainTextProvider() { + return array( + array(''), + array('abcdefg'), + array('1234567890123456'), + array('The quick brown fox'), + array('12345678901234567890123456789012'), + array('The quick brown fox jumped over the lazy dog.'), + array('何らかのマルチバイト文字列'), + ); + } + /** * Test that changing the key causes decryption to fail. * diff --git a/lib/Cake/Test/Case/View/Helper/SessionHelperTest.php b/lib/Cake/Test/Case/View/Helper/SessionHelperTest.php index 5ff9737c63a..99b807d01c0 100644 --- a/lib/Cake/Test/Case/View/Helper/SessionHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/SessionHelperTest.php @@ -150,6 +150,18 @@ public function testFlash() { $this->assertFalse($this->Session->check('Message.bare')); } +/** + * Test the flash method works without any params being passed + * + * @return void + */ + public function testFlashWithNoParams() { + $result = $this->Session->flash(); + $expected = '
This is a calling
'; + $this->assertEquals($expected, $result); + $this->assertFalse($this->Session->check('Message.flash')); + } + /** * test flash() with the attributes. * diff --git a/lib/Cake/TestSuite/CakeTestRunner.php b/lib/Cake/TestSuite/CakeTestRunner.php index b8ded6f098f..dfd2321de85 100644 --- a/lib/Cake/TestSuite/CakeTestRunner.php +++ b/lib/Cake/TestSuite/CakeTestRunner.php @@ -97,7 +97,7 @@ protected function createTestResult() { * @throws RuntimeException When fixture manager class cannot be loaded. */ protected function _getFixtureManager($arguments) { - if (isset($arguments['fixtureManager'])) { + if (!empty($arguments['fixtureManager'])) { App::uses($arguments['fixtureManager'], 'TestSuite'); if (class_exists($arguments['fixtureManager'])) { return new $arguments['fixtureManager']; diff --git a/lib/Cake/Utility/Security.php b/lib/Cake/Utility/Security.php index 75c5ad4c944..322b29d9e0d 100644 --- a/lib/Cake/Utility/Security.php +++ b/lib/Cake/Utility/Security.php @@ -184,9 +184,12 @@ public static function randomBytes($length) { if (function_exists('openssl_random_pseudo_bytes')) { return openssl_random_pseudo_bytes($length); } + if (function_exists('mcrypt_create_iv')) { + return mcrypt_create_iv($length, MCRYPT_DEV_URANDOM); + } trigger_error( 'You do not have a safe source of random data available. ' . - 'Install either the openssl extension, or paragonie/random_compat. ' . + 'Install either the openssl extension, the mcrypt extension, or paragonie/random_compat. ' . 'Falling back to an insecure random source.', E_USER_WARNING ); @@ -206,7 +209,7 @@ public static function randomBytes($length) { * for sensitive data. Additionally this method does *not* work in environments * where suhosin is enabled. * - * Instead you should use Security::rijndael() when you need strong + * Instead you should use Security::encrypt() when you need strong * encryption. * * @param string $text Encrypted string to decrypt, normal string to encrypt @@ -349,12 +352,24 @@ public static function encrypt($plain, $key, $hmacSalt = null) { // Generate the encryption and hmac key. $key = substr(hash('sha256', $key . $hmacSalt), 0, 32); - $algorithm = MCRYPT_RIJNDAEL_128; - $mode = MCRYPT_MODE_CBC; + if (Configure::read('Security.useOpenSsl')) { + $method = 'AES-256-CBC'; + $ivSize = openssl_cipher_iv_length($method); + $iv = openssl_random_pseudo_bytes($ivSize); + $padLength = (int)ceil((strlen($plain) ?: 1) / $ivSize) * $ivSize; + $ciphertext = openssl_encrypt(str_pad($plain, $padLength, "\0"), $method, $key, true, $iv); + // Remove the PKCS#7 padding block for compatibility with mcrypt. + // Since we have padded the provided data with \0, the final block contains only padded bytes. + // So it can be removed safely. + $ciphertext = $iv . substr($ciphertext, 0, -$ivSize); + } else { + $algorithm = MCRYPT_RIJNDAEL_128; + $mode = MCRYPT_MODE_CBC; + $ivSize = mcrypt_get_iv_size($algorithm, $mode); + $iv = mcrypt_create_iv($ivSize, MCRYPT_DEV_URANDOM); + $ciphertext = $iv . mcrypt_encrypt($algorithm, $key, $plain, $mode, $iv); + } - $ivSize = mcrypt_get_iv_size($algorithm, $mode); - $iv = mcrypt_create_iv($ivSize, MCRYPT_DEV_URANDOM); - $ciphertext = $iv . mcrypt_encrypt($algorithm, $key, $plain, $mode, $iv); $hmac = hash_hmac('sha256', $ciphertext, $key); return $hmac . $ciphertext; } @@ -404,13 +419,23 @@ public static function decrypt($cipher, $key, $hmacSalt = null) { return false; } - $algorithm = MCRYPT_RIJNDAEL_128; - $mode = MCRYPT_MODE_CBC; - $ivSize = mcrypt_get_iv_size($algorithm, $mode); + if (Configure::read('Security.useOpenSsl')) { + $method = 'AES-256-CBC'; + $ivSize = openssl_cipher_iv_length($method); + $iv = substr($cipher, 0, $ivSize); + $cipher = substr($cipher, $ivSize); + // Regenerate PKCS#7 padding block + $padding = openssl_encrypt('', $method, $key, true, substr($cipher, -$ivSize)); + $plain = openssl_decrypt($cipher . $padding, $method, $key, true, $iv); + } else { + $algorithm = MCRYPT_RIJNDAEL_128; + $mode = MCRYPT_MODE_CBC; + $ivSize = mcrypt_get_iv_size($algorithm, $mode); + $iv = substr($cipher, 0, $ivSize); + $cipher = substr($cipher, $ivSize); + $plain = mcrypt_decrypt($algorithm, $key, $cipher, $mode, $iv); + } - $iv = substr($cipher, 0, $ivSize); - $cipher = substr($cipher, $ivSize); - $plain = mcrypt_decrypt($algorithm, $key, $cipher, $mode, $iv); return rtrim($plain, "\0"); } diff --git a/lib/Cake/VERSION.txt b/lib/Cake/VERSION.txt index 77edd71dfbc..42ae9cdb36a 100644 --- a/lib/Cake/VERSION.txt +++ b/lib/Cake/VERSION.txt @@ -17,4 +17,4 @@ // @license https://opensource.org/licenses/mit-license.php MIT License // +--------------------------------------------------------------------------------------------+ // //////////////////////////////////////////////////////////////////////////////////////////////////// -2.10.7 +2.10.8