目前认为MD5是部分不安全的。考虑到这一点,我想知道使用哪种机制来保护密码。

这个问题,“双重哈希”密码是否比只哈希一次更不安全? 建议哈希多次可能是一个好主意,而如何实现个别文件的密码保护?建议使用盐。

我用的是PHP。我想要一个安全快捷的密码加密系统。对一个密码进行一百万次哈希运算可能更安全,但也更慢。如何在速度和安全之间取得良好的平衡?此外,我更希望结果具有恒定数量的字符。

哈希机制必须在PHP中可用 必须是安全的 它可以使用盐(在这种情况下,所有的盐都一样好吗?有没有办法生产出好的盐?)

另外,我是否应该在数据库中存储两个字段(例如,一个使用MD5,另一个使用SHA)?这会让它更安全还是更不安全?

以防我不够清楚,我想知道要使用哪个哈希函数,以及如何选择一个好的盐,以便拥有一个安全和快速的密码保护机制。

没有完全涵盖我的问题的相关问题:

PHP中的SHA和MD5有什么区别 简单密码加密 为asp.net存储密钥和密码的安全方法 如何在Tomcat 5.5中实现加盐密码


当前回答

最后,在数学上,双重哈希没有任何好处。然而,在实践中,它对于防止基于彩虹表的攻击是有用的。换句话说,它并不比使用salt进行哈希更有好处,后者在应用程序或服务器上占用的处理器时间要少得多。

其他回答

在可预见的将来,SHA1和salt应该足够了(当然,这取决于您是为Fort Knox编写代码还是为购物清单编写登录系统)。如果SHA1不够好,可以使用SHA256。

The idea of a salt is to throw the hashing results off balance, so to say. It is known, for example, that the MD5-hash of an empty string is d41d8cd98f00b204e9800998ecf8427e. So, if someone with good enough a memory would see that hash and know that it's the hash of an empty string. But if the string is salted (say, with the string "MY_PERSONAL_SALT"), the hash for the 'empty string' (i.e. "MY_PERSONAL_SALT") becomes aeac2612626724592271634fb14d3ea6, hence non-obvious to backtrace. What I'm trying to say, that it's better to use any salt, than not to. Therefore, it's not too much of an importance to know which salt to use.

实际上有一些网站就是这样做的——你可以给它一个(md5)哈希值,它会吐出一个已知的明文来生成那个特定的哈希值。如果您要访问存储普通md5-哈希值的数据库,那么为这样的服务的管理员输入哈希值并登录就很简单了。但是,如果密码被加密,这样的服务将变得无效。

此外,双重哈希通常被认为是不好的方法,因为它减少了结果空间。所有流行哈希值都是固定长度的。因此,这个固定长度的值是有限的,结果变化不大。这可以被视为另一种形式的盐,但我不建议这样做。

我不会以两种不同的方式存储哈希密码,因为这样的话,系统至少和正在使用的最弱的哈希算法一样弱。

我使用的是Phpass,这是一个简单的单文件PHP类,可以在几乎每个PHP项目中非常容易地实现。参见The H。

默认情况下,它使用在Phpass中实现的最强可用加密,即bcrypt,并回落到其他加密,直到MD5,以提供向后兼容的框架,如Wordpress。

返回的散列可以按原样存储在数据库中。生成哈希的示例使用如下:

$t_hasher = new PasswordHash(8, FALSE);
$hash = $t_hasher->HashPassword($password);

要验证密码,可以使用:

$t_hasher = new PasswordHash(8, FALSE);
$check = $t_hasher->CheckPassword($password, $hash);

我在这里找到了关于这个问题的完美主题:https://crackstation.net/hashing-security.htm,我希望你能从中受益,这里还有源代码,可以防止基于时间的攻击。

<?php
/*
 * Password hashing with PBKDF2.
 * Author: havoc AT defuse.ca
 * www: https://defuse.ca/php-pbkdf2.htm
 */

// These constants may be changed without breaking existing hashes.
define("PBKDF2_HASH_ALGORITHM", "sha256");
define("PBKDF2_ITERATIONS", 1000);
define("PBKDF2_SALT_BYTES", 24);
define("PBKDF2_HASH_BYTES", 24);

define("HASH_SECTIONS", 4);
define("HASH_ALGORITHM_INDEX", 0);
define("HASH_ITERATION_INDEX", 1);
define("HASH_SALT_INDEX", 2);
define("HASH_PBKDF2_INDEX", 3);

function create_hash($password)
{
    // format: algorithm:iterations:salt:hash
    $salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTES, MCRYPT_DEV_URANDOM));
    return PBKDF2_HASH_ALGORITHM . ":" . PBKDF2_ITERATIONS . ":" .  $salt . ":" . 
        base64_encode(pbkdf2(
            PBKDF2_HASH_ALGORITHM,
            $password,
            $salt,
            PBKDF2_ITERATIONS,
            PBKDF2_HASH_BYTES,
            true
        ));
}

function validate_password($password, $good_hash)
{
    $params = explode(":", $good_hash);
    if(count($params) < HASH_SECTIONS)
       return false; 
    $pbkdf2 = base64_decode($params[HASH_PBKDF2_INDEX]);
    return slow_equals(
        $pbkdf2,
        pbkdf2(
            $params[HASH_ALGORITHM_INDEX],
            $password,
            $params[HASH_SALT_INDEX],
            (int)$params[HASH_ITERATION_INDEX],
            strlen($pbkdf2),
            true
        )
    );
}

// Compares two strings $a and $b in length-constant time.
function slow_equals($a, $b)
{
    $diff = strlen($a) ^ strlen($b);
    for($i = 0; $i < strlen($a) && $i < strlen($b); $i++)
    {
        $diff |= ord($a[$i]) ^ ord($b[$i]);
    }
    return $diff === 0; 
}

/*
 * PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
 * $algorithm - The hash algorithm to use. Recommended: SHA256
 * $password - The password.
 * $salt - A salt that is unique to the password.
 * $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.
 * $key_length - The length of the derived key in bytes.
 * $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
 * Returns: A $key_length-byte key derived from the password and salt.
 *
 * Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
 *
 * This implementation of PBKDF2 was originally created by https://defuse.ca
 * With improvements by http://www.variations-of-shadow.com
 */
function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
{
    $algorithm = strtolower($algorithm);
    if(!in_array($algorithm, hash_algos(), true))
        die('PBKDF2 ERROR: Invalid hash algorithm.');
    if($count <= 0 || $key_length <= 0)
        die('PBKDF2 ERROR: Invalid parameters.');

    $hash_length = strlen(hash($algorithm, "", true));
    $block_count = ceil($key_length / $hash_length);

    $output = "";
    for($i = 1; $i <= $block_count; $i++) {
        // $i encoded as 4 bytes, big endian.
        $last = $salt . pack("N", $i);
        // first iteration
        $last = $xorsum = hash_hmac($algorithm, $last, $password, true);
        // perform the other $count - 1 iterations
        for ($j = 1; $j < $count; $j++) {
            $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
        }
        $output .= $xorsum;
    }

    if($raw_output)
        return substr($output, 0, $key_length);
    else
        return bin2hex(substr($output, 0, $key_length));
}
?>

从PHP 5.5开始,PHP就有了简单安全的散列和验证密码的函数password_hash()和password_verify()

$password = 'anna';
$hash = password_hash($password, PASSWORD_DEFAULT);
$expensiveHash = password_hash($password, PASSWORD_DEFAULT, array('cost' => 20));

password_verify('anna', $hash); //Returns true
password_verify('anna', $expensiveHash); //Also returns true
password_verify('elsa', $hash); //Returns false

当使用password_hash()时,它生成一个随机的盐并将其包含在输出的哈希中(以及使用的代价和算法)。password_verify()然后读取该哈希并确定使用的盐和加密方法,并根据提供的明文密码验证它。

提供PASSWORD_DEFAULT指示PHP使用已安装的PHP版本的默认哈希算法。具体哪种算法在未来的版本中会随着时间的推移而改变,因此它将始终是可用的最强算法之一。

不断增加的成本(默认为10)使得哈希更难使用暴力,但也意味着生成哈希并根据它们验证密码将为服务器的CPU带来更多的工作。

请注意,即使默认的哈希算法可能会改变,旧的哈希将继续进行验证,因为所使用的算法存储在哈希中,password_verify()会捕获它。