我的Bcrypt加密是否足够安全?

时间:2013-08-23 18:38:02

标签: php encryption bcrypt

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

这是我编程的方式:

  1. 当用户注册时,将使用以下详细信息创建salt:
    • 唯一的用户ID(mySQL中的主键)
    • 用户emailaddress
    • 当前(微)时间戳
    • 网站配置中定义的一些随机密钥
  2. 这个盐正被哈希拉成sha512键。
  3. 创建salt之后,使用Bcrypt:password + sha512 salt(worklevel 10,($ 2a $ 10 ...))对以下字符串进行哈希处理。
  4. 然后我跳过Bcrypt输出的前5个字符($ 2a $ 10),我将剩余的字符串保存到数据库中。
  5. 当用户尝试登录时,我首先检查用户名是否存在。如果是,我使用check()函数检查密码是否正确。

    我使用this Bcrypt class加密并检查。

    你能告诉我这种加密和验证方式是否适合大型项目?

    此致

    Jan Willem

3 个答案:

答案 0 :(得分:3)

  1. 从任何特定输入中获取盐绝对没有意义。实际上,这只能用于削弱它。从与要散列的值有关系的任何值派生的salt不是salt,它只是一个改变的散列算法。盐是完全(伪)随机噪音,期间。他们唯一的观点是使输入唯一;最独特的价值是随机噪音。
  2. 如果从一个好的伪随机数生成器派生盐,则无需哈希。这只能减少其熵。
  3. 您应该将整个结果存储在数据库中(包括$2a$10)。如果你正确地做到这一点,这个哈希几乎是不可能蛮力的。省略这条信息只会使你的工作变得更加困难,对于潜在的攻击者而言,这并不会使其变得更加困难。

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

  4. 对旧版本使用PHP的新password_hash APIpassword_compat垫片,这些版本经过了充分测试,非常“先进”。

答案 1 :(得分:0)

首先,不要使用bcrypt。请改用PBKDF2。这将允许您增加进行离线暴力攻击的难度。 [不是说bcrypt不会,它们基本相同,但PBKDF2支持任何散列函数,而不仅仅是Blowfish]

其次,这是你应该怎么做的。

  1. 为Salt
  2. 随机生成(不要以任何东西为基础)128位随机数
  3. 将Salt作为自己的参数存储在数据库中。请记住,salt的目的是使得两次哈希相同的密码不会产生相同的结果。
  4. 选择一个好的哈希算法。我的建议是使用SHA-256或SHA-512。这是您将用作PBKDF2函数的一部分。
  5. 做一些研究,弄清楚什么是好的轮数使密码的散列需要1秒。请记住,如果我们有一个96个字符的密码密钥空间和至少8个字符的宽度,并且每个排列需要1秒钟来计算,那么攻击者就会哭。这很好的部分是随着时间的推移,当计算机变得更快时,您可以重新评估此值并将哈希值再散列几次以使其达到新的1秒要求。 示例:假设在现代计算机上12轮SHA-256需要1秒。在5年内,计算机现在如此之快,以至于12轮需要20毫秒。但是16轮在硬件上需要1秒钟。只需将哈希值再散列4次,现在将其保持在1秒。用户从来不知道发生了这种情况,因为您不必要求他们更改密码。
  6. 您可以将密码存储在Shadow格式中,但是如果您使用的是数据库,则只需使用数据库字段。解析字符串比只知道要给你什么的数据库慢。
  7. 现在我想指出系统中的计时攻击。如果您告诉攻击者用户名不存在或者返回错误所需的时间与返回成功所用的时间不同(并且更短),则您可能会发生侧通道攻击。其他信息可以使攻击者更容易枚举您系统上的用户帐户(如果用户名基于电子邮件,则他们现在可以直接对用户进行网络钓鱼攻击)。

答案 2 :(得分: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。由于我们需要盐,我在另一个数据库中搜索,其中存储了哈希和盐。然后返回salt并且我们能够使用相同的函数验证给定的密码(encrypt())。
  7. 这就是它。这样安全吗?

    Jan Willem