Hello,
Today I came across an issue with SecurityManager encrypt/decrypt functionality.
The following simple code fails :
./protected/yiic shell
>> $data1 = "hi there";
>> $encdata = Yii::app()->securityManager->encrypt($data1);
>> $data2 = Yii::app()->securityManager->decrypt($encdata);
PHP Warning: mcrypt_generic_init(): Iv size incorrect; supplied length: 14, needed: 8 in /home/kefah/NetBeansProjects/yalla3/yii/framework/base/CSecurityManager.php on line 197
PHP Stack trace:
PHP 1. {main}() /home/kefah/NetBeansProjects/yalla3/customer/protected/yiic:0
PHP 2. require_once() /home/kefah/NetBeansProjects/yalla3/customer/protected/yiic:4
PHP 3. require_once() /home/kefah/NetBeansProjects/yalla3/customer/protected/yiic.php:7
PHP 4. CApplication->run() /home/kefah/NetBeansProjects/yalla3/yii/framework/yiic.php:33
PHP 5. CConsoleApplication->processRequest() /home/kefah/NetBeansProjects/yalla3/yii/framework/base/CApplication.php:135
PHP 6. CConsoleCommandRunner->run() /home/kefah/NetBeansProjects/yalla3/yii/framework/console/CConsoleApplication.php:88
PHP 7. ShellCommand->run() /home/kefah/NetBeansProjects/yalla3/yii/framework/console/CConsoleCommandRunner.php:62
PHP 8. ShellCommand->runShell() /home/kefah/NetBeansProjects/yalla3/yii/framework/cli/commands/ShellCommand.php:99
PHP 9. eval() /home/kefah/NetBeansProjects/yalla3/yii/framework/cli/commands/ShellCommand.php:147
PHP 10. CSecurityManager->decrypt() /home/kefah/NetBeansProjects/yalla3/yii/framework/cli/commands/ShellCommand.php(147) : eval()'d code:1
PHP 11. mcrypt_generic_init() /home/kefah/NetBeansProjects/yalla3/yii/framework/base/CSecurityManager.php:197
I use Fedora 13, with PHP 5.3.2. I have php mbstring enabled with mbstring.func_overload = 7 in /etc/php.ini as my php apps are heavily internationalized.
Checking up the SecurityManager sourcecode I found that it relies on strlen and substr, the use of those php functions is not i18n or binary safe.
Applying the following patch solved the problem for me. I think its safe to apply this patch, as it will guarantee proper handling of i18n or binary data within utf8 enabled php setups. It shouldn’t affect other php setups.
diff --git a/yii/framework/base/CSecurityManager.php b/yii/framework/base/CSecurityManager.php
index 4fe77af..2ebb28f 100644
--- a/yii/framework/base/CSecurityManager.php
+++ b/yii/framework/base/CSecurityManager.php
@@ -171,7 +171,7 @@ class CSecurityManager extends CApplicationComponent
public function encrypt($data,$key=null)
{
$module=$this->openCryptModule();
- $key=substr($key===null ? md5($this->getEncryptionKey()) : $key,0,mcrypt_enc_get_key_size($module));
+ $key=mb_substr($key===null ? md5($this->getEncryptionKey()) : $key,0,mcrypt_enc_get_key_size($module), 'latin1');
srand();
$iv=mcrypt_create_iv(mcrypt_enc_get_iv_size($module), MCRYPT_RAND);
mcrypt_generic_init($module,$key,$iv);
@@ -191,11 +191,11 @@ class CSecurityManager extends CApplicationComponent
public function decrypt($data,$key=null)
{
$module=$this->openCryptModule();
- $key=substr($key===null ? md5($this->getEncryptionKey()) : $key,0,mcrypt_enc_get_key_size($module));
+ $key=mb_substr($key===null ? md5($this->getEncryptionKey()) : $key,0,mcrypt_enc_get_key_size($module), 'latin1');
$ivSize=mcrypt_enc_get_iv_size($module);
- $iv=substr($data,0,$ivSize);
+ $iv=mb_substr($data,0,$ivSize, 'latin1');
mcrypt_generic_init($module,$key,$iv);
- $decrypted=mdecrypt_generic($module,substr($data,$ivSize));
+ $decrypted=mdecrypt_generic($module,mb_substr($data,$ivSize, mb_strlen($data, 'latin1'), 'latin1'));
mcrypt_generic_deinit($module);
mcrypt_module_close($module);
return rtrim($decrypted,"\0");
@@ -245,11 +245,11 @@ class CSecurityManager extends CApplicationComponent
*/
public function validateData($data,$key=null)
{
- $len=strlen($this->computeHMAC('test'));
- if(strlen($data)>=$len)
+ $len=mb_strlen($this->computeHMAC('test'), 'latin1');
+ if(mb_strlen($data, 'latin1')>=$len)
{
- $hmac=substr($data,0,$len);
- $data2=substr($data,$len);
+ $hmac=mb_substr($data,0,$len, 'latin1');
+ $data2=mb_substr($data,$len, mb_strlen($data, 'latin1'), 'latin1');
return $hmac===$this->computeHMAC($data2,$key)?$data2:false;
}
else
@@ -281,6 +281,6 @@ class CSecurityManager extends CApplicationComponent
$func='md5';
}
$key=str_pad($func($key), 64, chr(0));
- return $func((str_repeat(chr(0x5C), 64) ^ substr($key, 0, 64)) . pack($pack, $func((str_repeat(chr(0x36), 64) ^ substr($key, 0, 64)) . $data)));
+ return $func((str_repeat(chr(0x5C), 64) ^ mb_substr($key, 0, 64, 'latin1')) . pack($pack, $func((str_repeat(chr(0x36), 64) ^ mb_substr($key, 0, 64, 'latin1'))
}
}
Here is the patched securitymanager working:
>> $data1 = "hi there";
>> $encdata = Yii::app()->securityManager->encrypt($data1);
>> $data2 = Yii::app()->securityManager->decrypt($encdata);
>> echo $data2;
hi there