【问题标题】:PHP encryption / deryption with recovery code带恢复代码的 PHP 加密/解密
【发布时间】:2015-11-12 16:09:43
【问题描述】:

我尝试重建here解释的加密流程

加密

$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 的“初始化向量”? this 正确吗?

【问题讨论】:

    标签: php encryption mcrypt


    【解决方案1】:

    部分分析

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

    • 增加 PBKDF2 函数的迭代次数。现在5000已经很低了。尝试一百万。
    • 具有 256 位块大小的 Rijndael 不如具有 128 位块大小(即 AES)的 Rijndael 分析得好。你应该使用MCRYPT_RIJNDAEL_128。这也将提高与其他实现的兼容性。
    • 您没有使用可以检测(恶意)对您的密文进行操纵的密文身份验证。请参阅 this example 了解执行此操作的简单 OpenSSL 方法。
    • 您正在使用 MCrypt 的默认零填充,如果您的明文以 0x00 字节结尾,这将是不好的。从解密的消息中删除这些内容时,您需要小心。

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

    初始化向量只需要是不可预测的,而不是秘密的。如果 IV 生成是可预测的,则存在可以利用系统的攻击,但 openssl_random_pseudo_bytes 应该是好的。它没有正确初始化的可能性很小,并且您将使用非“强”随机字节(检查第二个参数),但即使如此,取决于您的系统架构,IV 的可预测性可能无法利用。

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

    整体分析

    该代码似乎实现了a good protocol described by Thomas Pronin,但协议有点不完整。因为没有什么可以阻止攻击者提交随机的Ru 并用它覆盖所有用户的值。

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

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

    并将其存储在服务器上,以备不时之需。

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

    【讨论】:

    • 是的,更正它在服务器上运行,但我可以使用 javasript 生成它,然后将其发送到服务器
    • 协议是好的,但是在恢复的过程中出现了问题。我已经扩展了我的答案。
    • bin2hex(Ru) 获取可读代码是否存在安全问题?
    • 取决于你想用它做什么。单独的编码没有问题。
    • 即打印出来。我想用户在从 openssl_random_pseudo_bytes 输入字符时可能会遇到一些问题
    猜你喜欢
    • 2011-07-14
    • 2011-06-13
    • 1970-01-01
    • 2012-09-30
    • 2019-08-13
    • 2014-03-26
    • 2013-04-17
    • 2010-12-13
    相关资源
    最近更新 更多