我想知道此功能(部分取自〜2岁的phpBB版本)是否足够好。
如果没有,为什么?
以及您将如何更改它(使现有用户无缝过渡)?
hash_pwd()的结果将保存在数据库中。
function hash_pwd($password)
{
$itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
$random_state = $this->unique_id();
$random = '';
$count = 6;
if (($fh = @fopen('/dev/urandom', 'rb')))
{
$random = fread($fh, $count);
fclose($fh);
}
if (strlen($random) < $count)
{
$random = '';
for ($i = 0; $i < $count; $i += 16)
{
$random_state = md5($this->unique_id() . $random_state);
$random .= pack('H*', md5($random_state));
}
$random = substr($random, 0, $count);
}
$hash = $this->_hash_crypt_private($password, $this->_hash_gensalt_private($random, $itoa64), $itoa64);
if (strlen($hash) == 34)
{
return $hash;
}
return false;
}
function unique_id()
{
$val = microtime();
$val = md5($val);
return substr($val, 4, 16);
}
function _hash_crypt_private($password, $setting, &$itoa64)
{
$output = '*';
// Check for correct hash
if (substr($setting, 0, 3) != '$H$')
{
return $output;
}
$count_log2 = strpos($itoa64, $setting[3]);
if ($count_log2 < 7 || $count_log2 > 30)
{
return $output;
}
$count = 1 << $count_log2;
$salt = substr($setting, 4, 8);
if (strlen($salt) != 8)
{
return $output;
}
/**
* We're kind of forced to use MD5 here since it's the only
* cryptographic primitive available in all versions of PHP
* currently in use. To implement our own low-level crypto
* in PHP would result in much worse performance and
* consequently in lower iteration counts and hashes that are
* quicker to crack (by non-PHP code).
*/
if (PHP_VERSION >= 5)
{
$hash = md5($salt . $password, true);
do
{
$hash = md5($hash . $password, true);
}
while (--$count);
}
else
{
$hash = pack('H*', md5($salt . $password));
do
{
$hash = pack('H*', md5($hash . $password));
}
while (--$count);
}
$output = substr($setting, 0, 12);
$output .= $this->_hash_encode64($hash, 16, $itoa64);
return $output;
}
function _hash_gensalt_private($input, &$itoa64, $iteration_count_log2 = 6)
{
if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
{
$iteration_count_log2 = 8;
}
$output = '$H$';
$output .= $itoa64[min($iteration_count_log2 + ((PHP_VERSION >= 5) ? 5 : 3), 30)];
$output .= $this->_hash_encode64($input, 6, $itoa64);
return $output;
}
function _hash_encode64($input, $count, &$itoa64)
{
$output = '';
$i = 0;
do
{
$value = ord($input[$i++]);
$output .= $itoa64[$value & 0x3f];
if ($i < $count)
{
$value |= ord($input[$i]) << 8;
}
$output .= $itoa64[($value >> 6) & 0x3f];
if ($i++ >= $count)
{
break;
}
if ($i < $count)
{
$value |= ord($input[$i]) << 16;
}
$output .= $itoa64[($value >> 12) & 0x3f];
if ($i++ >= $count)
{
break;
}
$output .= $itoa64[($value >> 18) & 0x3f];
}
while ($i < $count);
return $output;
}
最佳答案
您提供的代码是PHPASS的端口,特别是“便携式”算法。注意portable
的限定条件。如果您将phpass
作为第二个构造函数参数传递,则这仅适用于true
库。从此以后,此答案中的phpass
仅将引用引用可移植算法,而不是库本身。如果您未显式指定portable
,则默认情况下该库将执行bcrypt。
PHPBB团队并没有自己开发(很好),而是直接从phpass移植了(可争论)。
我们应该在这里提出几个问题:
不好吗?
简短的答案是“不,这还不错”。它提供了很好的安全性。如果您现在对此有代码,那么我就不会着急将其删除。适用于大多数用途。但是话虽如此,如果您开始一个我不会选择的新项目,那么还有更好的选择。
有哪些弱点?
pbkdf2
:phpass
算法使用 hash()
,而pbkdf2()
使用 hash_hmac()
。现在,HMAC内部为每个调用运行2个散列,但是PHP实现仅花费对hash()
的单个调用执行的1.6倍(C不好吗?)。因此,我们在hash_hmac
执行2个散列所需的时间中,有62%的时间从hash()
获得2个散列。那是什么意思?好吧,对于给定的运行时,
pbkdf2
将比phpass
算法多运行约37.5%的散列。给定时间内更多的哈希==好,因为它导致执行更多的计算。因此,当使用相同的原语(在这种情况下为
pbkdf2
)时,37.5%
大约比phpass
强md5
。但是pbkdf2
也可以采用更强大的基元。因此,我们可以将pbkdf2
与sha512
结合使用,以获得比phpass
算法非常重要的优势(主要是因为sha512
是比md5
运算量更大的更难算法)。这意味着
pbkdf2
不仅可以在相同的时间内生成更多的计算,而且还可以生成更困难的计算。话虽如此,差异并不算太大。这是非常可测量的,并且
pbkdf2
绝对比phpass
“更强”。 bcrypt
:比较起来很难。但是,让我们看一下它的表面。 phpass
使用md5
和PHP中的循环。 pbkdf2
使用任何原语(在C中)和PHP中的循环。 bcrypt
在C语言中全部使用自定义算法(这意味着它与任何可用的散列算法都不同)。因此,最棒的是,bcrypt
仅对算法全部在C中这一事实具有明显的优势。这允许每单位时间进行更多的“计算”。从而使其成为一种更有效的慢速算法(在给定的运行时中进行更多的计算)。但是,与执行多少次计算一样重要的是计算的质量。这可能是一篇完整的研究论文,但总而言之,可以归结为一个事实,即bcrypt内部使用的计算比普通的哈希函数执行起来困难得多。
bcrypt
的更强本质的一个示例是bcrypt
使用的内部状态比正常的哈希函数大得多的事实。 SHA512
使用512位内部状态针对1024位块进行计算。 bcrypt
使用大约32kb的内部状态来针对576位的单个块进行计算。 bcrypt
的内部状态比SHA512
(以及md5
和phpass
)大得多的事实,在一定程度上说明了bcrypt
的更强本质。 应该避免
对于新项目,绝对是。不是说它很坏。不是。事实证明,那里有更强大的算法(数量级)。那么为什么不使用它们呢?
为了进一步证明
bcrypt
更强,请查看Slides from Password13 (PDF),它启动了25个GPU集群来破解密码哈希。以下是相关结果:md5($password)
sha1($password)
md5crypt
(与phpass非常相似,费用为10):bcrypt
,费用为5
注意:所有可能的8个字符的密码都使用94个字符集:
a-zA-Z0-9~`!@#$%^&*()_+-={}|[]\:";'<>,.?/
底线
因此,如果您要编写新代码,请毫无疑问使用
bcrypt
。如果现在正在生产中有phpass
或pbkdf2
,则可能要升级,但这并不是明确的“您非常脆弱”。关于php - 这在PHP中是一个很好的哈希密码功能吗?如果没有,为什么不呢?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/16042128/