我尝试重新构建这里解释的加密流
$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
是随机生成的,所以迭代次数可以很低,但它仍然是一个昂贵的操作。您应该限制恢复操作的使用,以减少拒绝服务攻击。