我正确使用PHP的crypt()函数吗?

时间:2010-09-29 11:03:55

标签: php security encryption hash salt

我一直在使用PHP的crypt()作为在我的数据库中存储和验证密码的方法。我对其他东西使用散列,但crypt()用于密码。文档不是很好,似乎有很多争论。我正在使用河豚和两种盐来加密密码并将其存储在数据库中。在我存储salt和加密密码之前(如盐腌哈希),但实现了它的冗余,因为salt是加密密码字符串的一部分。

我对彩虹表攻击如何在crypt()上工作有点困惑,无论如何从安全的角度来看这看起来是正确的。我使用第二个盐来附加密码以增加短密码的熵,可能是矫枉过正,但为什么不呢?

function crypt_password($password) {
if ($password) {
    //find the longest valid salt allowed by server
    $max_salt = CRYPT_SALT_LENGTH;

    //blowfish hashing with a salt as follows: "$2a$", a two digit cost parameter, "$", and 22 base 64
    $blowfish = '$2a$10$';

    //get the longest salt, could set to 22 crypt ignores extra data
    $salt = get_salt ( $max_salt );

    //get a second salt to strengthen password
    $salt2 = get_salt ( 30 ); //set to whatever


    //append salt2 data to the password, and crypt using salt, results in a 60 char output
    $crypt_pass = crypt ( $password . $salt2, $blowfish . $salt );

    //insert crypt pass along with salt2 into database.
    $sql = "insert into database....";

    return true;
    }
}  


function get_salt($length) {
$options = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./';

$salt = '';

for($i = 0; $i <= $length; $i ++) {
    $options = str_shuffle ( $options );
    $salt .= $options [rand ( 0, 63 )];
}
return $salt;
}

function verify_password($input_password)
{
if($input_password)
{
    //get stored crypt pass,and salt2 from the database
    $stored_password = 'somethingfromdatabase';
    $stored_salt2 = 'somethingelsefromdatabase';

    //compare the crypt of input+stored_salt2 to the stored crypt password
    if (crypt($input_password . $stored_salt2, $stored_password) == $stored_password) {
        //authenticated
        return true;
    }
    else return false;
}
else return false;
}

5 个答案:

答案 0 :(得分:15)

你真的应该看看PHPASS:http://www.openwall.com/phpass/这是一个使用crypt()的密码哈希框架,用于Wordpress和phpBB等项目。

本网站上还有一篇关于使用crypt()进行密码散列,盐析和拉伸的优秀文章:http://www.openwall.com/articles/PHP-Users-Passwords

更新: 目前还有PHPASS库的替代品。在下一版本的PHP中,有一些用于散列和验证密码的特殊功能(使用bcrypt):http://www.php.net/manual/en/ref.password.php。有一个兼容性库,可以为PHP 5.3.7 +实现这些功能:https://github.com/ircmaxell/password_compat

答案 1 :(得分:11)

您对crypt()的使用很好。 crypt($input, $stored) == $stored是设计使用的方式。

您的get_salt()功能不是很好,因为它使用的是常用的rand()功能。您应该考虑使用更强大的随机函数,例如openssl_random_pseudo_bytes()

答案 2 :(得分:3)

彩虹表的想法是攻击者可以在家里制作一张包含所有可能密码及其哈希值的表格。

E.g。

PASSWORD HASH
iloveSO  gjroewjgo
password knbnogjwm
secret   gjroehghe
jbieber  rewgroewj

使用此表,攻击者可以快速将任何哈希转换为密码。彩虹表使用了一些技巧,因此不是所有的哈希值都必须存储,但它仍然可以预先计算所有哈希值。

通过使用盐,即使使用密码存储它,也会使这更难。攻击者不必在字典中对每个单词进行散列,而是必须使用每个单词对每个单词进行散列。使用足够长的盐,这就提供了足够的组合,使得计算所有这些哈希值变得不可行。

因此,盐不是一个额外的密码,只有应用程序知道,它意味着更改散列函数,使其不标准。

答案 3 :(得分:2)

这是对crypt()的误用,因为您使用的是已弃用的原语。 Blowfish非常古老,twofish是替代品,甚至是旧的因为三鱼几乎已经定型。您应该使用sha2系列的成员,sha256或sha512都是不错的选择。 crypt()可以与sha256或sha512一起使用,你应该分别使用CRYPT_SHA256 CRYPT_SHA512参数。

此外,您的盐的熵/尺寸比非常小,您只使用字母数字集,这是一个笑话,因为字母数字彩虹表是最常见的。您应该使用base256的完整字节,我建议使用长度为256字节的salt。请记住,根据定义,所有散列函数都是二进制安全的,因此您不必担心空字节等。

答案 4 :(得分:0)

将SHA-512(如果可用)与包含time()和openssl_random_pseudo_bytes()的salt一起使用。 Crypt是统一/高效的,因为它返回使用散列字符串插入的salt。