Improve cookie encryption

- use MAC to validate content before decryption
- create unique IV for every cookie

Signed-off-by: Michal Čihař <michal@cihar.com>
This commit is contained in:
Michal Čihař 2016-07-08 17:15:09 +02:00
parent 59e0f3dee4
commit 95b7b7d6dd

View File

@ -77,8 +77,8 @@ class AuthenticationCookie extends AuthenticationPlugin
$login_link = '<br /><br />[ ' .
sprintf(
'<a href="%s" class="ajax login-link">%s</a>',
$GLOBALS['cfg']['PmaAbsoluteUri'],
'<a href="%s" class="ajax login-link">%s</a>',
$GLOBALS['cfg']['PmaAbsoluteUri'],
__('Log in')
)
. ' ]';
@ -636,7 +636,7 @@ class AuthenticationCookie extends AuthenticationPlugin
*/
public function blowfishEncrypt($data, $secret)
{
global $iv;
$iv = $this->createIV();
if (! function_exists('mcrypt_encrypt')) {
/**
* This library uses mcrypt when available, so
@ -647,9 +647,9 @@ class AuthenticationCookie extends AuthenticationPlugin
include_once "./libraries/phpseclib/Crypt/AES.php";
$cipher = new Crypt_AES(CRYPT_AES_MODE_ECB);
$cipher->setKey($secret);
return base64_encode($cipher->encrypt($data));
$result = base64_encode($cipher->encrypt($data));
} else {
return base64_encode(
$result = base64_encode(
mcrypt_encrypt(
MCRYPT_BLOWFISH,
$secret,
@ -659,6 +659,13 @@ class AuthenticationCookie extends AuthenticationPlugin
)
);
}
return json_encode(
array(
'iv' => base64_encode($iv),
'mac' => sha1($result . $secret),
'payload' => $result,
)
);
}
/**
@ -668,29 +675,77 @@ class AuthenticationCookie extends AuthenticationPlugin
* @param string $encdata encrypted data
* @param string $secret the secret
*
* @return string original data
* @return string|bool original data, false on error
*/
public function blowfishDecrypt($encdata, $secret)
{
global $iv;
$data = json_decode($encdata, true);
if (! is_array($data) || ! isset($data['mac']) || ! isset($data['iv']) || ! isset($data['payload'])) {
return false;
}
$newmac = sha1($data['payload'] . $secret);
if (! hash_equals($data['mac'], $newmac)) {
return false;
}
if (! function_exists('mcrypt_encrypt')) {
include_once "./libraries/phpseclib/Crypt/AES.php";
$cipher = new Crypt_AES(CRYPT_AES_MODE_ECB);
$cipher->setIV(base64_decode($data['iv']));
$cipher->setKey($secret);
return $cipher->decrypt(base64_decode($encdata));
return $cipher->decrypt(base64_decode($data['payload']));
} else {
$data = base64_decode($encdata);
$decrypted = mcrypt_decrypt(
MCRYPT_BLOWFISH,
$secret,
$data,
base64_decode($data['payload']),
MCRYPT_MODE_CBC,
$iv
base64_decode($data['iv'])
);
return trim($decrypted);
}
}
/**
* Returns size of IV for encryption.
*
* @return int
*/
public function getIVSize()
{
include_once "./libraries/phpseclib/Crypt/AES.php";
$cipher = new Crypt_AES(CRYPT_AES_MODE_ECB);
return $cipher->block_size;
}
/**
* Initialization
* Store the initialization vector because it will be needed for
* further decryption. I don't think necessary to have one iv
* per server so I don't put the server number in the cookie name.
*
* @return void
*/
public function createIV()
{
if (function_exists('mcrypt_create_iv')) {
srand((double) microtime() * 1000000);
$td = mcrypt_module_open(MCRYPT_BLOWFISH, '', MCRYPT_MODE_CBC, '');
if ($td === false) {
PMA_fatalError(__('Failed to use Blowfish from mcrypt!'));
}
return mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
} else {
include_once "./libraries/phpseclib/Crypt/Random.php";
return crypt_random_string(
$this->getIVSize()
);
}
}
/**
* This method is called when any PluginManager to which the observer
* is attached calls PluginManager::notify()