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

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

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

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

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

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

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

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


当前回答

在可预见的将来,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));
}
?>

好吧 在fitsy,我们需要盐 盐必须是独一无二的 我们来生成它

   /**
     * Generating string
     * @param $size
     * @return string
     */
    function Uniwur_string($size){
        $text = md5(uniqid(rand(), TRUE));
        RETURN substr($text, 0, $size);
    }

我们还需要哈希值 我使用sha512 它是最好的,而且是用PHP编写的

   /**
     * Hashing string
     * @param $string
     * @return string
     */
    function hash($string){
        return hash('sha512', $string);
    }

所以现在我们可以使用这个函数来生成安全的密码

// generating unique password
$password = Uniwur_string(20); // or you can add manual password
// generating 32 character salt
$salt = Uniwur_string(32);
// now we can manipulate this informations

// hashin salt for safe
$hash_salt = hash($salt);
// hashing password
$hash_psw = hash($password.$hash_salt);

现在我们需要在数据库中保存$hash_psw变量值和$salt变量

对于授权,我们将使用相同的步骤…

这是保护客户密码的最好方法……

另外,最后两个步骤你可以使用自己的算法… 但是要确保将来可以生成这个散列密码 当您需要授权用户…

从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()会捕获它。

需要记住的事情

关于PHP密码加密已经说了很多,其中大多数都是非常好的建议,但是在开始使用PHP进行密码加密之前,请确保您已经实现了以下内容或准备实现以下内容。

服务器

港口

无论您的加密技术有多好,如果您不能正确地保护运行PHP和DB的服务器,那么您所有的努力都是徒劳的。大多数服务器的功能相对相同,它们都有指定的端口,允许您通过ftp或shell远程访问它们。确保您更改了活动的任何远程连接的默认端口。如果不这样做,实际上就使攻击者在访问您的系统时少做了一步。

用户名

在这个世界上,最好不要使用admin、root或类似的用户名。此外,如果你是在一个基于unix的系统上,不要让根帐户登录可访问,它应该总是只有sudo。

密码

你告诉你的用户要设置好的密码以避免被黑客攻击,你也要这么做。当后门大开着的时候,费劲地锁上前门又有什么意义呢?

数据库

服务器

理想情况下,您希望DB和APPLICATION位于不同的服务器上。由于成本原因,这并不总是可行的,但它确实具有一定的安全性,因为攻击者必须通过两个步骤才能完全访问系统。

USER

始终让您的应用程序拥有自己的帐户来访问DB,并且只给它所需的特权。

然后为您创建一个单独的用户帐户,该帐户不存储在服务器上的任何地方,甚至不存储在应用程序中。

比如总是不要做这个根或类似的东西。

密码

遵循与所有好密码相同的原则。另外,不要在同一系统上的任何SERVER或DB帐户上重复使用相同的密码。

PHP

密码

永远不要在你的数据库中存储密码,而是存储哈希和唯一的盐,我将在后面解释为什么。

哈希

单向哈希!!!!!!!永远不要以一种可以反转的方式哈希密码,哈希值应该是一种方式,这意味着你不反转它们并将它们与密码进行比较,而是以同样的方式哈希输入的密码并比较两个哈希值。这意味着即使攻击者访问了数据库,他也不知道实际的密码是什么,只知道其结果哈希值。这意味着在最糟糕的情况下为用户提供更多的安全性。

有很多不错的散列函数(password_hash, hash等等),但是为了使散列有效,您需要选择一个好的算法。(bcrypt和类似的算法都是不错的算法。)

当哈希速度是关键时,越慢越能抵抗暴力攻击。

哈希中最常见的错误之一是哈希值对用户来说不是唯一的。这主要是因为盐不是唯一产生的。

在散列之前,密码应该始终加盐。Salting在密码中添加一个随机字符串,这样类似的密码在DB中就不会出现相同的密码。然而,如果盐对每个用户都不是唯一的(例如:你使用了硬编码的盐),那么你几乎已经让你的盐变得毫无价值。因为一旦攻击者找到了一个密码盐,他就有了所有密码盐。

当你创建一个salt时,确保它是唯一的密码,然后将完成的散列和salt存储在你的DB中。这将使攻击者在获得访问权限之前必须单独破解每个盐和哈希。这对攻击者来说意味着更多的工作和时间。

用户创建密码

如果用户通过前端创建密码,则意味着必须将密码发送到服务器。这就产生了一个安全问题,因为这意味着未加密的密码将被发送到服务器,如果攻击者能够监听和访问,那么PHP中的所有安全性都是毫无价值的。始终安全地传输数据,这是通过SSL完成的,但即使SSL也不是完美无缺的(OpenSSL的Heartbleed缺陷就是一个例子)。

还要让用户创建一个安全的密码,这很简单,应该一直这样做,用户最终会感激它。

最后,无论你采取什么安全措施,没有什么是100%安全的,保护的技术越先进,攻击就会变得越先进。但是,遵循这些步骤将使您的站点更加安全,并且攻击者更不愿意进行攻击。

下面是一个PHP类,它可以轻松地为密码创建散列和盐

http://git.io/mSJqpw