Skip to content

Commit

Permalink
Session: hash weak secrets to make them more secure.
Browse files Browse the repository at this point in the history
  • Loading branch information
daschl committed Jan 3, 2012
1 parent 14de7bf commit 986adca
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 6 deletions.
45 changes: 43 additions & 2 deletions storage/session/strategy/Encrypt.php
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
* cookies (or generally on the client side) and this class is no exception to the rule. It allows * cookies (or generally on the client side) and this class is no exception to the rule. It allows
* you to store client side data in a more secure way, but 100% security can't be achieved. * you to store client side data in a more secure way, but 100% security can't be achieved.
* *
* Also note that if you provide a secret that is shorter than the maximum key length of the algorithm
* used, the secret will be hashed to make it more secure. This also means that if you want to use your
* own hashing algorithm, make sure it has the maximum key length of the algorithm used. See the
* `Encrypt::_hashSecret()` method for more information on this.
*
* @link http://php.net/manual/en/book.mcrypt.php The mcrypt extension. * @link http://php.net/manual/en/book.mcrypt.php The mcrypt extension.
* @link http://www.php.net/manual/en/mcrypt.ciphers.php List of supported ciphers. * @link http://www.php.net/manual/en/mcrypt.ciphers.php List of supported ciphers.
* @link http://www.php.net/manual/en/mcrypt.constants.php List of supported modes. * @link http://www.php.net/manual/en/mcrypt.constants.php List of supported modes.
Expand Down Expand Up @@ -162,9 +167,9 @@ public static function enabled() {
*/ */
protected function _encrypt($decrypted = array()) { protected function _encrypt($decrypted = array()) {
$cipher = $this->_config['cipher']; $cipher = $this->_config['cipher'];
$secret = $this->_config['secret'];
$mode = $this->_config['mode']; $mode = $this->_config['mode'];
$vector = $this->_config['vector']; $vector = $this->_config['vector'];
$secret = $this->_hashSecret($this->_config['secret']);


$encrypted = mcrypt_encrypt($cipher, $secret, serialize($decrypted), $mode, $vector); $encrypted = mcrypt_encrypt($cipher, $secret, serialize($decrypted), $mode, $vector);
$data = base64_encode($encrypted) . base64_encode($vector); $data = base64_encode($encrypted) . base64_encode($vector);
Expand All @@ -180,9 +185,9 @@ protected function _encrypt($decrypted = array()) {
*/ */
protected function _decrypt($encrypted) { protected function _decrypt($encrypted) {
$cipher = $this->_config['cipher']; $cipher = $this->_config['cipher'];
$secret = $this->_config['secret'];
$mode = $this->_config['mode']; $mode = $this->_config['mode'];
$vector = $this->_config['vector']; $vector = $this->_config['vector'];
$secret = $this->_hashSecret($this->_config['secret']);


$vectorSize = strlen(base64_encode(str_repeat(" ", static::_vectorSize($cipher, $mode)))); $vectorSize = strlen(base64_encode(str_repeat(" ", static::_vectorSize($cipher, $mode))));
$vector = base64_decode(substr($encrypted, -$vectorSize)); $vector = base64_decode(substr($encrypted, -$vectorSize));
Expand All @@ -194,6 +199,42 @@ protected function _decrypt($encrypted) {
return $data; return $data;
} }


/**
* Hashes the given secret to make it more secure.
*
* This method figures out the appropriate key size for the chosen encryption algorithm and
* then tries to find the best hashing method for it. Note that if the key has already the
* needed length, it is considered to be hashed (secure) already and is therefore not hashed
* again. This lets you change the hashing method in your own code if you like.
*
* The default `MCRYPT_RIJNDAEL_256` key should be 32 byte long and therefore `sha256` is the
* preferred hashing algorithm.
*
* @link http://www.php.net/manual/de/function.mcrypt-get-key-size.php
* @param string $key The possibly too weak key.
* @return string The hashed (raw) key.
*/
protected function _hashSecret($key) {
$size = mcrypt_get_key_size($this->_config['cipher'], $this->_config['mode']);

if(strlen($key) >= $size) {
return $key;
}

switch($size) {
case $size >= 32:
$algorithm = 'sha256';
break;
case $size >= 20:
$algorithm = 'sha1';
break;
default:
$algorithm = 'md5';
}

return hash($algorithm, $key, true);
}

/** /**
* Generates an initialization vector. * Generates an initialization vector.
* *
Expand Down
7 changes: 3 additions & 4 deletions tests/cases/storage/session/strategy/EncryptTest.php
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ class EncryptTest extends \lithium\test\Unit {
public $secret = 'foobar'; public $secret = 'foobar';


/** /**
* Skip the test if Mcrypt extension is unavailable. * Skip the test if the mcrypt extension is unavailable.
*
* @return void
*/ */
public function skip() { public function skip() {
$this->skipIf(!Encrypt::enabled(), 'The Mcrypt extension is not installed or enabled.'); $this->skipIf(!Encrypt::enabled(), 'The Mcrypt extension is not installed or enabled.');
Expand All @@ -28,7 +26,7 @@ public function setUp() {
$this->mock = 'lithium\tests\mocks\storage\session\strategy\MockCookieSession'; $this->mock = 'lithium\tests\mocks\storage\session\strategy\MockCookieSession';
MockCookieSession::reset(); MockCookieSession::reset();
} }

public function testConstructException() { public function testConstructException() {
$this->expectException('/Encrypt strategy requires a secret key./'); $this->expectException('/Encrypt strategy requires a secret key./');
$encrypt = new Encrypt(); $encrypt = new Encrypt();
Expand Down Expand Up @@ -96,6 +94,7 @@ public function testDelete() {
$result = $encrypt->read($key, array('class' => $this->mock)); $result = $encrypt->read($key, array('class' => $this->mock));
$this->assertFalse($result); $this->assertFalse($result);
} }

} }


?> ?>

0 comments on commit 986adca

Please sign in to comment.