PHP加密/解密与恢复代码


PHP encryption / deryption with recovery code

我尝试重新构建这里解释的加密流

$password = 'pass123456';  //user password
$message = 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.';
$Ku = openssl_random_pseudo_bytes(256 / 8); //user-specific symmetric key
$Ru = openssl_random_pseudo_bytes(256 / 8); //recovery code
$Su = openssl_random_pseudo_bytes(256 / 8); //user salt ###STORED ON SERVER###
$hash = hash_pbkdf2('sha256', $password, $Su, 5000, 256 / 8, true);
$split = str_split($hash, 16);
$Vu = $split[0]; //password verification token ###STORED ON SERVER###
$Ek = $split[1]; //key to encrypt Ku

$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$Eu = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $Ek, $Ku, MCRYPT_MODE_CBC, $iv); ###STORED ON SERVER###
$Fu = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $Ru, $Ku, MCRYPT_MODE_CBC, $iv); ###STORED ON SERVER###
$encryptedMessage = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $Ku, $message, MCRYPT_MODE_CBC, $iv);
解密

$hash = hash_pbkdf2('sha256', ###ENTERED PASSWORD###, ###STORED Su###, 5000, 256 / 8, true);
$split = str_split($hash, 16);
$Vu = $split[0];
$Ek = $split[1];

if($Vu != ###STORED Vu###) {
   => wrong password
} else {
   => correct password
}

$Ku = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $EK, ###STORED Eu###, MCRYPT_MODE_CBC, ###STORED iv###);
$message = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $Ku, $encryptedMessage, MCRYPT_MODE_CBC, ###STORED iv###);

如果我丢失了我的密码,我可以恢复"Ku":

$Ku = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, ###REVOVERY KEY###, ###STORED Fu###, MCRYPT_MODE_CBC, ###STORED iv###);

上面的代码安全吗?openssl_random_pseudo_bytes是否足够随机,或者是否有更好的可能性,即使用鼠标移动来生成初始化数?

应该如何"初始化向量"为mcrypt处理?这是正确的吗?

零件分析

看起来不错,但是你可能想

  • 增加PBKDF2函数的迭代。5000美元现在已经很低了。
  • 256位块大小的Rijndael不如128位块大小的Rijndael (AES)分析得好。你应该用MCRYPT_RIJNDAEL_128。这也将提高与其他实现的兼容性。
  • 您没有使用可以检测(恶意)操纵您的密文的密文身份验证。下面是一个简单的OpenSSL方法。
  • 你正在使用MCrypt的默认零填充,如果你的明文以0x00字节结束,这是不好的。当你从你的解密信息中删除这些信息时,你需要小心。

openssl_random_pseudo_bytes是否足够随机,或者是否有更好的可能性,即使用鼠标移动来生成初始化数?

初始化向量只需要是不可预测的,而不是秘密的。如果IV代是可预测的,则存在可以利用系统的攻击,但openssl_random_pseudo_bytes应该是好的。有一个小的机会,它没有正确地初始化,你将使用非"强"随机字节(检查第二个参数),但即使这样,根据你的系统架构,IV的可预测性可能不会被利用。

我还想知道如何在PHP中获得鼠标移动,因为我假设这是在服务器上运行的。

整体分析

这段代码似乎实现了Thomas Pronin描述的一个很好的协议,但协议有点不完整。因为没有什么可以阻止攻击者提交一个随机的Ru,并用它覆盖所有用户的值。

在创建帐户(和更改密码)期间,您还需要为恢复密钥创建验证值。我认为创建

就足够了
$VRu = hash_pbkdf2('sha256', $Ru, $Su, 1000, 256 / 8, true);

并将其存储在服务器上,以备恢复时使用。

由于Ru是随机生成的,所以迭代次数可以很低,但它仍然是一个昂贵的操作。您应该限制恢复操作的使用,以减少拒绝服务攻击。