Permalink
Browse files

Storage 1.2: improvements, bug fixes and unit test

  • Loading branch information...
diegolamonica committed Aug 12, 2012
1 parent 9e22cca commit 36fa979d2381546ac8cea923e6bbf36e13f2accc
Showing with 559 additions and 24 deletions.
  1. +46 −21 classes/Storage.php
  2. +3 −1 classes/Storage/CookieStorage.php
  3. +246 −0 unit-test/cookieStorage.php
  4. +244 −0 unit-test/sessionStorage.php
  5. +20 −2 unit-test/unit-test.common.php
View
@@ -15,9 +15,14 @@ class Storage extends Debugger{
* - Removed SessionStorage and CookieStorage classes from this file.
* - defined methods scope
*
* V 1.2
* - Added public method enableEncryption()
* - Created Unit Test for CookieStorage
* ( reachable using this querystring ?core_info=unit-test&class=cookieStorage )
* - Enforced the decrypt method: now the metod tries to detect if the stored data is encrypted before decrypting it
*/
const VERSION = 1.1;
const VERSION = 1.2;
/**
* the specialized Storage handler.
@@ -36,6 +41,7 @@ class Storage extends Debugger{
* @var string
*/
private $saltKey = '';
private $secureData = true;
public function __construct(){
@@ -45,8 +51,16 @@ public function __construct(){
!defined('STORAGE_METHOD') && define('STORAGE_METHOD', SESSION_STORAGE);
$this->setEncryptionKey(STORAGE_ENCRYPTION_KEY);
$this->setStorage(STORAGE_METHOD);
$this->enableEncryption(!defined('STORAGE_SECURE_DATA') || defined('STORAGE_SECURE_DATA') && (STORAGE_SECURE_DATA=='true' || STORAGE_SECURE_DATA === true) );
}
/**
* Enable / Disable the encription of variables
* @param bool $status
*/
public function enableEncryption($status){
$this->secureData = $status;
}
/**
* Defines the salt key.
@@ -132,7 +146,8 @@ public function read($key){
* i will try to decode it and to deserialize it.
*/
$value = ($this->decrypt($value));
if($this->is_serialized($value, $retVal)) $value = $retVal;
if(!is_null($value) && $this->is_serialized($value, $retVal)) $value = $retVal;
}
return $value;
}
@@ -162,7 +177,7 @@ public function debug(){
*/
private function encrypt($decrypted) {
if(function_exists('mcrypt_decrypt')&& (!defined('STORAGE_SECURE_DATA') || defined('STORAGE_SECURE_DATA') && STORAGE_SECURE_DATA=='true')){
if(function_exists('mcrypt_decrypt')&& $this->secureData){
$password = $this->encryptionKey;
$salt = $this->saltKey;
// Build a 256-bit $key which is a SHA256 hash of $salt and $password.
@@ -186,24 +201,34 @@ private function encrypt($decrypted) {
*/
private function decrypt($encrypted) {
if(function_exists('mcrypt_decrypt') && (!defined('STORAGE_SECURE_DATA') || defined('STORAGE_SECURE_DATA') && STORAGE_SECURE_DATA=='true') ){
$password = $this->encryptionKey;
$salt = $this->saltKey;
// Build a 256-bit $key which is a SHA256 hash of $salt and $password.
$key = hash('SHA256', $salt . $password, true);
// Retrieve $iv which is the first 22 characters plus ==, base64_decoded.
$iv = base64_decode(substr($encrypted, 0, 22) . '==');
// Remove $iv from $encrypted.
$encrypted = substr($encrypted, 22);
// Decrypt the data. rtrim won't corrupt the data because the last 32 characters are the md5 hash; thus any \0 character has to be padding.
$decrypted = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($encrypted), MCRYPT_MODE_CBC, $iv), "\0\4");
// Retrieve $hash which is the last 32 characters of $decrypted.
$hash = substr($decrypted, -32);
// Remove the last 32 characters from $decrypted.
$decrypted = substr($decrypted, 0, -32);
// Integrity check. If this fails, either the data is corrupted, or the password/salt was incorrect.
if (md5($decrypted) != $hash) return false;
// Yay!
if(function_exists('mcrypt_decrypt') && $this->secureData){
/*
* If the value is serialized then it's not encrypted value.
*/
if(!$this->is_serialized($encrypted)){
$password = $this->encryptionKey;
$salt = $this->saltKey;
// Build a 256-bit $key which is a SHA256 hash of $salt and $password.
$key = hash('SHA256', $salt . $password, true);
// Retrieve $iv which is the first 22 characters plus ==, base64_decoded.
$iv_base64 = substr($encrypted, 0, 22);
if (strlen($iv_base64) != 22) return null;
$iv = base64_decode($iv_base64 . '==');
// Remove $iv from $encrypted.
$encrypted = substr($encrypted, 22);
// Decrypt the data. rtrim won't corrupt the data because the last 32 characters are the md5 hash; thus any \0 character has to be padding.
$decrypted = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($encrypted), MCRYPT_MODE_CBC, $iv), "\0\4");
// Retrieve $hash which is the last 32 characters of $decrypted.
$hash = substr($decrypted, -32);
// Remove the last 32 characters from $decrypted.
$decrypted = substr($decrypted, 0, -32);
// Integrity check. If this fails, either the data is corrupted, or the password/salt was incorrect.
if (md5($decrypted) != $hash) return null;
// Yay!
}else{
$decrypted = null;
}
return $decrypted;
}else{
return $encrypted;
@@ -114,6 +114,8 @@ public function destroy($key = ''){
}
}
public function debug();
public function debug(){
}
}
?>
View
@@ -0,0 +1,246 @@
<?php
require dirname(__FILE__) ."/unit-test.common.php";
function CookieStorage_V1_UnitTest(){
if(!isset($_GET['step'])) $_GET['step'] = 1;
echo("<!DOCTYPE HTML5>");
echo("<html>");
echo("<head>");
echo("<title>Unit Test for class Storage (using CookieStorage) v 1.0</title>" );
echo("<style type=\"text/css\">*{ padding: 0.2em; } .success{ color: #040; background-color: #efe; } .error{ color: #400; background-color: #fee;} code{ background-color: #eee; border: 1px solid #ccc; color: #444; } </style>");
echo("</head>");
echo("<body>");
echo("<h1>Unit Test for class Storage (using CookieStorage)</h1>");
echo("<p>Starting the checks</p>");
$encrypted1 = 'cookieEncrypted1';
$encrypted2 = 'cookieEncrypted2';
$clearText = 'cookieClearText';
$arrayCookie = 'arrayCookie';
$arrayValues = array(
'first-item' => 'lorem',
'second-item'=> 'ipsum'
);
if($_GET['step']==1){
/*
* Step 1
*/
$aut = new AlphaUnitTest('CookieStorage-1');
$aut->throwException(false);
$aut->stopOnFirstError(true);
// Simple Variable Settings
$storage = ClassFactory::get('Storage');
$storage->setStorage(COOKIE_STORAGE);
$storage->enableEncryption(true);
$storage->setSalt('AlphaFramework');
/*
* Test #1: Writing a cookie
*/
$storage->write($encrypted1, $encrypted1);
$aut->throwIfNot(
$aut->equals( $storage->read($encrypted1), $encrypted1),
"Cookie correctly setted",
"Cookie not set correctly",
$storage->read($encrypted1)
);
/*
* Test #2: checking cookie encryption
*/
$aut->throwIf(
$aut->isNull( $storage->read($encrypted1)),
"Cookie is correctly encrypted",
"Cookie is not encrypted",
$_COOKIE[$encrypted1]
);
/*
* Test #3: checking unencrypted cookie
*/
$storage->enableEncryption(false);
$storage->write($clearText, $clearText);
$aut->throwIfNot(
$aut->equals( $storage->read($clearText), unserialize($_COOKIE[$clearText])),
"Cookie is correctly stored as clean value",
"Cookie is encrypted!",
$storage->read($clearText),
$_COOKIE[$clearText]
);
/*
* Test #4: re-enabling encryption and reading the unencrypted value
*
*/
$storage->enableEncryption(true);
$aut->throwIfNot(
$aut->equals( $storage->read($encrypted1), $encrypted1),
"Cookie read correctly",
"Cookie is not read correctly!",
$storage->read($encrypted1),
$_COOKIE[$encrypted1]
);
/*
* Test #5: Changing the salt
*/
$storage->setSalt('Alpha-Framework');
$aut->throwIf(
$aut->equals( $storage->read($encrypted1), $encrypted1),
"Cookie not read because it has a different salt key",
"The salt has been changed, however the cookie is read correctly! ",
$storage->read($encrypted1),
$_COOKIE[$encrypted1]
);
/*
* Test #6: Writing cookie with the new different salt key
*/
$storage->write($encrypted2, $encrypted2);
$aut->throwIfNot(
$aut->equals( $storage->read($encrypted2), $encrypted2),
"Cookie with new salt has been read correctly",
"The cookie has a different salt!",
$storage->read($encrypted2),
$_COOKIE[$encrypted2]
);
/*
* Test #7: Reading the unencrypted cookie with encryption enabled
*/
$aut->throwIfNot(
$aut->isNull( $storage->read($clearText)),
"Cookie with different salt return null value!",
"The cookie has a different salt!",
$storage->read($clearText),
$_COOKIE[$clearText]
);
/*
* Test #8: Writing and Reading Array in cookie
*/
$storage->write($arrayCookie, $arrayValues);
$aut->throwIf(
$aut->isNull($storage->read($arrayCookie)),
"Array Cookie correctly written",
"Array Cookie seems to be not set",
$storage->read($arrayCookie));
/*
* Test #9: Checking array cookie data type
*/
$aut->throwIfNot(
$aut->isArray($storage->read($arrayCookie)),
"Array Cookie is obviously an array",
"Array cookie is not an array",
$storage->read($arrayCookie));
/*
* Test #10: Checking array cookie value
*/
$aut->throwIfNot(
$aut->equals($storage->read($arrayCookie), $arrayValues),
"Data in Array Cookie is as expected",
"Array Cookie contents is not what expected",
$storage->read($arrayCookie));
echo("<dl>");
echo("<dt>Tests Passed:</dt><dd class=\"success\">" . $aut->passed() . "</dd>");
echo("<dt>Tests Failed:</dt><dd class=\"error\">" . $aut->failed() . "</dd>");
echo("</dl>");
if($aut->isFullCompliant()){
$url = $_SERVER['REQUEST_URI'];
$url .= '&step=2';
echo("<a href=\"$url\">Go to the next step 2</a>");
}
}else{
$aut = new AlphaUnitTest('CookieStorage-2');
$aut->throwException(false);
$aut->stopOnFirstError(true);
$storage = ClassFactory::get('Storage');
$storage->setStorage(COOKIE_STORAGE);
$storage->setSalt('AlphaFramework');
/*
* Test #1: Read the encrypted cookie
*/
$storage->enableEncryption(true);
$aut->throwIfNot(
$aut->equals( $storage->read($encrypted1), $encrypted1),
"Cookie stored in step 1 read correctly",
"Cookie has not persistence! Check browser settings.",
$storage->read($encrypted1),
$_COOKIE[$encrypted1]
);
/*
* Test #2: Read the clean cookie
*/
$storage->enableEncryption(false);
$aut->throwIfNot(
$aut->equals( $storage->read($clearText), unserialize($_COOKIE[$clearText])),
"Cookie is correctly stored as clean value",
"Cookie is encrypted!",
$storage->read($clearText),
$_COOKIE[$clearText]
);
/*
* Test #3: Read the encoded cookie with different salt key
*/
$storage->enableEncryption(true);
$storage->setSalt('Alpha-Framework');
$aut->throwIfNot(
$aut->isNull( $storage->read($encrypted1) ),
"Cookie has different salt, cannot be read",
"Cookie has been read unexpectedly!",
$storage->read($encrypted1),
$_COOKIE[$encrypted1]
);
/*
* Test #4: Read the encoded cookie with right salt key
*/
$aut->throwIfNot(
$aut->equals( $storage->read($encrypted2), $encrypted2),
"Cookie correctly read!",
"Cookie has different salt, cannot be read",
$storage->read($encrypted2),
$_COOKIE[$encrypted2]
);
echo("<dl>");
echo("<dt>Tests Passed:</dt><dd class=\"success\">" . $aut->passed() . "</dd>");
echo("<dt>Tests Failed:</dt><dd class=\"error\">" . $aut->failed() . "</dd>");
echo("</dl>");
if($aut->isFullCompliant()){
$url = $_SERVER['REQUEST_URI'];
$url = preg_replace( '#'.preg_quote('&step=2','#').'#', '', $url);
echo("<a href=\"$url\">Go to the previous step 1</a>");
}
}
echo("</body>");
echo("</html>");
ob_flush();
}
CookieStorage_V1_UnitTest();
?>
Oops, something went wrong.

0 comments on commit 36fa979

Please sign in to comment.