1

我正在开发一个项目,该项目在保存密码等方面需要非常先进的安全系统。所以我的问题是,这种保存密码的方式是否足够安全?

这是我编程的方式:

  1. 当用户注册时,将根据以下详细信息创建盐:
    • 唯一的用户 ID(mySQL 中的主键)
    • 用户电子邮件地址
    • 当前(微)时间戳
    • 网站配置中定义的一些随机密钥
  2. 这个盐被散列到一个 sha512 密钥中。
  3. 创建 salt 后,使用 Bcrypt 对以下字符串进行哈希处理:密码 + sha512 salt (worklevel 10, ($2a$10...))。
  4. 然后我跳过 Bcrypt 输出的前 5 个字符($2a$10),并将剩余的字符串保存到数据库中。

当用户尝试登录时,我首先检查用户名是否存在。如果是,我使用 check() 函数检查密码是否正确。

我使用这个 Bcrypt 类来加密和检查。

你们能告诉我这种加密和验证方式对于一个大项目是否足够好?

问候,

扬·威廉

4

3 回答 3

3
  1. 从任何特定输入中获取盐绝对没有意义。事实上,这只能起到削弱它的作用。从与要散列的值有关系的任何值派生的盐不是盐,它只是一种改变的散列算法。盐完全是(伪)随机噪声,周期。他们唯一的目的是使输入唯一;最独特的价值是随机噪声。
  2. 如果您从一个好的伪随机数生成器中派生盐,则无需对其进行散列。这只能用来减少它的熵。
  3. 您应该将整个结果存储在数据库中(包括$2a$10)。如果您做得正确,那么该哈希几乎不可能按原样进行暴力破解。忽略那条信息只会让你的工作变得更加困难,但对于潜在的攻击者来说并不会变得更加困难。

    存储该值可以让您查看创建哈希的算法并随着时间的推移对其进行升级。随着硬件变得更快,您应该增加算法的工作因子,并且随着更好的算法变得可用(你好 scrypt!),您应该升级使用的算法。有效的方法是在用户登录时检查哈希是否是使用最新和最大的。那时您就有机会重新散列明文密码并对其进行升级。

对旧版本使用 PHP 的新password_hashAPIpassword_compat shim,这些都经过良好测试并且完全“高级”。

于 2013-08-23T19:09:10.360 回答
0

First, don't use bcrypt. Use PBKDF2 instead. This will allow you to increase the difficulty of doing an offline brute force attack. [Not saying that bcrypt won't, they are essentially the same, but PBKDF2 supports any hashing function, not just Blowfish]

Second, here is how you should do this.

  1. Randomly Generate (don't base it on anything) a 128-bit random number for the Salt
  2. Store the Salt as it's own parameter in the database. Remember the purpose of the salt is to make it so that the same password hashed twice doesn't have the same result.
  3. Pick a good Hashing Algorithm. My recommendation, use SHA-256 or SHA-512. This is what you will use as part of the PBKDF2 function.
  4. Do some research, figure out what is a good number of rounds to make the hashing of the password take 1 second. Remember if we have a password keyspace of 96 characters and a minimum of 8 characters wide, and each permutation takes a required 1 second to calculate, then an attacker will cry. The nice part of this is that over time as computers become faster, you can reevaluate this value and hash the hash a few more times to bring it up to the new 1 second requirement. Example: Say that 12 rounds of SHA-256 takes 1 second on modern computers. In 5 years, the computers are now so fast that 12 rounds takes 20ms instead. But 16 rounds takes 1 second on the hardware then. Just hashing the hash 4 additional times now keeps it at 1 second. The user never knew this happened because you didn't have to ask them to change their password.
  5. You could store your password in the Shadow format, sure, but if you are using a database, just use database fields. You parsing a string is slower than the database just knowing what to give you.

Now I want to point out a timing attack in your system. If you tell an attacker that a username doesn't exist or the amount of time it takes to return an error is different (and shorter) than it takes to return a success, then you have a possibility for a side channel attack. Additional information can make it easier for an attacker to enumerate user accounts on your system (and if the username is based on Email, then they can now do a phishing attack against the user directly).

于 2013-08-23T19:17:27.340 回答
0

首先,我要感谢你们的快速响应,对我来说,让系统尽可能难以被黑客入侵对我来说非常重要。

根据您的建议,我制作了以下系统:

  1. 当用户注册时,会创建一个唯一的哈希:

    $unique_hash = hash('sha512', 一些定义的安全密钥(在配置中)。openssl_random_pseudo_bytes(50));

  2. 其次,创建盐:

    $salt = strtr(base64_encode(openssl_random_pseudo_bytes(50)), '+', '.');

  3. 在另一个数据库中的另一个表中,这两个变量被组合在一起,只有 $unique_hash 存储在 users 表的 users 行中。

  4. 然后我们创建加密密码,我使用这个函数:

    <?php
    const COUNT = 8192; 
    const KEY_LENGTH = 254; 
    const ALGORITHM = 'sha512';
    
    public static function encrypt($password, $salt, $algorithm = PBKDF2::ALGORITHM, $count = PBKDF2::COUNT, $key_length = PBKDF2::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++) 
    {
    
        $last = $salt . pack("N", $i);
        $last = $xorsum = hash_hmac($algorithm, $last, $password, true);
    
        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));
    
     }
         ?>
    
  5. 然后我们将此密码存储在默认用户表中。

  6. 最后一步; 登录。当用户尝试登录时,首先我检查给定的用户名是否存在于数据库中。当它出现时,我调用位于同一行的 $unique_hash。由于我们需要盐,我在另一个数据库中搜索,其中存储了哈希和盐。然后将返回盐,我们可以使用相同的函数 (encrypt()) 验证给定的密码。

这将是它。这足够安全吗?

扬·威廉

于 2013-08-26T10:08:52.693 回答