目前我有一个存储了md5密码的数据库,几年前这被认为比现在更加安全,并且它已经达到了密码需要更加安全的程度.
我在这里读了很多关于crypt,md5,hash,bcrypt等的帖子,并考虑使用下面的内容来比现在更好地“保护”密码.
我将使用哈希(“sha512”和两个盐的组合,第一个盐将是存储在诸如.htaccess的文件中的站点范围的盐,并且将为每个用户创建第二个盐.
这是我现在正在测试的一个例子:
的.htaccess
SetEnv SITEWIDE_SALT NeZa5Edabex?26Y#j5pr7VASpu$8UheVaREj$yA*59t*A$EdRUqer_prazepreTr
使用example.php
$currentpassword = //get password
$pepper = getenv('SITEWIDE_SALT');
$salt = microtime().ip2long($_SERVER['REMOTE_ADDR']);
$saltpepper = $salt.$pepper;
$password = hash("sha512", md5($currentpassword).$saltpepper);
显然,盐需要存储在一个单独的表中,以便检查将来插入的登录密码,但用户永远无法看到.你认为这是一个充分的方法吗?
解决方法:
好的,我们在这里讨论几点
>你在$salt中所拥有的不是盐.这是确定性的(意味着根本就没有随机性).如果您想要盐,请使用mcrypt_create_iv($size, MCRYPT_DEV_URANDOM)
或其他一些实际随机熵源.关键是它应该既独特又随意.请注意,它不需要加密安全随机…在绝对最差的情况下,我会做这样的事情:
function getRandomBytes($length) {
$bytes = '';
for ($i = 0; $i < $length; $i++) {
$bytes .= chr(mt_rand(0, 255));
}
return $bytes;
}
>正如@ Anony-Mousse指出的那样,从不将一个哈希函数的输出提供给另一个哈希函数而不将原始数据重新附加到它.相反,使用适当的迭代算法,如PBKDF2,PHPASS或CRYPT_BLOWFISH($2a $).
我的建议是使用带有河豚的crypt,因为它是目前PHP最好的:
function createBlowfishHash($password) {
$salt = to64(getRandomBytes(16));
$salt = '$2a$10$' . $salt;
$result = crypt($password, $salt);
}
然后使用如下方法验证:
function verifyBlowfishHash($password, $hash) {
return $hash == crypt($password, $hash);
}
(注意,to64是定义here的好方法).你也可以使用str_replace(”,’.’,base64_encode($salt)); …
我还建议你阅读以下两点:
> Fundamental difference between hashing and encrypting
> Many hash iterations, append salt every time?
编辑:回答迁移问题
好的,所以我意识到我的答案没有解决原始问题的迁移方面.所以这就是我如何解决它.
首先,构建一个临时函数,从原始的md5哈希创建一个新的河豚哈希,带有随机盐和前缀,以便我们以后可以检测到:
function migrateMD5Password($md5Hash) {
$salt = to64(getRandomBytes(16));
$salt = '$2a$10$' . $salt;
$hash = crypt($md5Hash, $salt);
return '$md5' . $hash;
}
现在,通过此函数运行所有现有的md5哈希,并将结果保存在数据库中.我们将自己的前缀放入,以便我们可以检测原始密码并添加额外的md5步骤.所以现在我们都迁移了.
接下来,创建另一个函数来验证密码,如果需要,使用新的哈希更新数据库:
function checkAndMigrateHash($password, $hash) {
if (substr($hash, 0, 4) == '$md5') {
// Migrate!
$hash = substr($hash, 4);
if (!verifyBlowfishHash(md5($password), $hash) {
return false;
}
// valid hash, so let's generate a new one
$newHash = createBlowfishHash($password);
saveUpdatedPasswordHash($newHash);
return true;
} else {
return verifyBlowfishHash($password, $hash);
}
}
这是我建议的原因有几点:
>它立即从数据库中获取md5()哈希值.
>它最终(每个用户的下一次登录)将哈希更新为更好的替代方案(一个很好理解的方法).
>在代码中很容易理解.
回答评论:
>盐不需要是随机的 – 我将你引导到RFC 2898 – Password Based Cryptography.即4.1节.我引述:
If there is no concern about interactions between multiple uses
of the same key (or a prefix of that key) with the password-
based encryption and authentication techniques supported for a
given password, then the salt may be generated at random and
need not be checked for a particular format by the party
receiving the salt. It should be at least eight octets (64
bits) long.
另外,
Note. If a random number generator or pseudorandom generator is not
available, a deterministic alternative for generating the salt (or
the random part of it) is to apply a password-based key derivation
function to the password and the message M to be processed.
可以使用伪随机生成器,为什么不使用它呢?
>您的解决方案与bcrypt相同吗?我找不到关于bcrypt究竟是什么的文档? – 我假设您已经阅读了bcrypt Wikipedia Article,并尝试更好地解释它.
BCrypt基于Blowfish分组密码.它从密码中获取密钥调度设置算法,并使用它来散列密码.好的原因是,Blowfish的设置算法设计得非常昂贵(这是使河豚如此强大的一部分的一部分).基本流程如下:
> 18个元素阵列(称为P盒,大小为32位)和4个2维阵列(称为S盒,每个具有256个条目,每个8位)用于通过使用预定静态值初始化阵列来设置调度.另外,64位状态被初始化为全0.
>传入的密钥是按顺序XOred所有18个P盒(如果它太短则旋转键).
>然后使用P框加密先前初始化的状态.
>步骤3产生的密文用于替换P1和P2(P阵列的前2个元素).
>重复步骤3,将结果放入P3和P4.这一直持续到P17和P18填充.
这是Blowfish Cipher的关键衍生物. BCrypt修改了这个:
> 64位状态初始化为盐的加密版本.
>相同
>然后使用P框加密先前初始化的(状态x或部分盐).
>相同
>相同
>然后,生成的设置用于加密密码64次.这就是BCrypt所回归的.
重点很简单:这是一个非常昂贵的算法,占用了大量的CPU时间.这是它应该被使用的真正原因.
我希望能够解决问题.