在PHP中更新旧的存储的md5密码以提高安全性

Dan*_*Dan 1 php security encryption passwords hash

目前我有一个存储了md5密码的数据库,几年前这被认为比现在更加安全,并且它已经达到了密码需要更加安全的程度.

我读了很多关于这里的职位crypt,md5,hash,bcrypt,等,并已经开始考虑使用的东西大致如下,以"安全"的密码比他们现在的线路.

我将使用hash("sha512"两种盐的组合,第一种盐将是存储在诸如.htaccess的文件中的全站点盐,并且将为每个用户创建第二种盐.

这里有一个例子大意就是我此刻的测试:

的.htaccess

SetEnv SITEWIDE_SALT NeZa5Edabex?26Y#j5pr7VASpu$8UheVaREj$yA*59t*A$EdRUqer_prazepreTr
Run Code Online (Sandbox Code Playgroud)

使用example.php

$currentpassword = //get password

$pepper = getenv('SITEWIDE_SALT');
$salt = microtime().ip2long($_SERVER['REMOTE_ADDR']);

$saltpepper = $salt.$pepper;

$password = hash("sha512", md5($currentpassword).$saltpepper);
Run Code Online (Sandbox Code Playgroud)

显然,盐需要存储在一个单独的表中,以便检查将来插入的登录密码,但用户永远无法看到.你认为这是一个充分的方法吗?

irc*_*ell 9

好的,我们在这里讨论几点

  1. 你所拥有的$salt不是盐.这是确定性的(意味着根本就没有随机性).如果你想要一个盐,使用任何一个mcrypt_create_iv($size, MCRYPT_DEV_URANDOM)或其他一些实际的随机熵源.关键是它应该既独特又随意.请注意,它不需要加密安全随机...在绝对最差的情况下,我会做这样的事情:

    function getRandomBytes($length) {
        $bytes = '';
        for ($i = 0; $i < $length; $i++) {
            $bytes .= chr(mt_rand(0, 255));
        }
        return $bytes;
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 正如@ Anony-Mousse指出的那样,永远不会将一个哈希函数的输出提供给另一个哈希函数而不将原始数据重新附加到它.相反,使用适当的迭代算法,如PBKDF2,PHPASSCRYPT_BLOWFISH($ 2a $).

    我的建议是使用cryptblowfish,因为它是PHP目前最好的:

    function createBlowfishHash($password) {
        $salt = to64(getRandomBytes(16));
        $salt = '$2a$10$' . $salt;
        $result = crypt($password, $salt);
    }
    
    Run Code Online (Sandbox Code Playgroud)

    然后使用如下方法验证:

    function verifyBlowfishHash($password, $hash) {
        return $hash == crypt($password, $hash);
    }
    
    Run Code Online (Sandbox Code Playgroud)

    (注意to64是定义一个很好的方法,在这里).你也可以用str_replace('+', '.', base64_encode($salt));......

我还建议你阅读以下两点:

编辑:回答迁移问题

好的,所以我意识到我的答案没有解决原始问题的迁移方面.所以这就是我如何解决它.

首先,构建一个临时函数,从原始的md5哈希创建一个新的河豚哈希,带有随机盐和前缀,以便我们以后可以检测到:

function migrateMD5Password($md5Hash) {
    $salt = to64(getRandomBytes(16));
    $salt = '$2a$10$' . $salt;
    $hash = crypt($md5Hash, $salt);
    return '$md5' . $hash;
}
Run Code Online (Sandbox Code Playgroud)

现在,通过此函数运行所有现有的md5哈希,并将结果保存在数据库中.我们将自己的前缀放入,以便我们可以检测原始密码并添加额外的md5步骤.所以现在我们都迁移了.

接下来,创建另一个函数来验证密码,如果需要,使用新的哈希更新数据库:

function checkAndMigrateHash($password, $hash) {
    if (substr($hash, 0, 4) == '$md5') {
        // Migrate!
        $hash = substr($hash, 4);
        if (!verifyBlowfishHash(md5($password), $hash) {
            return false;
        }
        // valid hash, so let's generate a new one
        $newHash = createBlowfishHash($password);
        saveUpdatedPasswordHash($newHash);
        return true;
    } else {
        return verifyBlowfishHash($password, $hash);
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我建议的原因有几点:

  1. 它会md5()立即从您的数据库中获取哈希值.
  2. 它最终(每个用户的下一次登录)将哈希更新为更好的替代方案(一个很好理解的方法).
  3. 在代码中很容易理解.

回答评论:

  1. 盐不需要是随机的 - 我将您引导至RFC 2898 - 基于密码的密码学.即第4.1节.我引述:

    如果不关心同一密钥(或该密钥的前缀)的多次使用与给定密码支持的基于密码的加密和认证技术之间的交互,则可以随机生成盐,无需检查对于接收盐的一方的特定格式.它应至少为八个八位位组(64位).

    另外,

    注意.如果随机数生成器或伪随机生成器不可用,则生成盐(或其随机部分)的确定性替代方法是将基于密码的密钥导出函数应用于密码和要处理的消息M.

    可以使用伪随机生成器,为什么不使用它呢?

  2. 你的解决方案和bcrypt一样吗?我找不到关于bcrypt究竟是什么的文档? - 我假设您已经阅读了bcrypt维基百科文章,并尝试更好地解释它.

    BCrypt基于Blowfish分组密码.它从密码中获取密钥调度设置算法,并使用它来散列密码.好的原因是,Blowfish的设置算法设计得非常昂贵(这是使河豚如此强大的一部分的一部分).基本流程如下:

    1. 18个元素阵列(称为P盒,大小为32位)和4个2维阵列(称为S盒,每个具有256个条目,每个8位)用于通过用预定静态值初始化阵列来设置调度.另外,64位状态被初始化为全0.

    2. 传入的密钥是按顺序XOred所有18个P盒(如果它太短,则旋转密钥).

    3. 然后使用P框来加密先前初始化的状态.

    4. 步骤3产生的密文用于替换P1和P2(P阵列的前2个元素).

    5. 重复步骤3,将结果放入P3和P4.这一直持续到P17和P18填充.

    这是Blowfish Cipher的关键衍生物.BCrypt修改了这个:

    1. 64位状态初始化为盐的加密版本.

    2. 相同

    3. 然后使用P盒加密先前初始化的(状态x或部分盐).

    4. 相同

    5. 相同

    6. 然后使用生成的设置将密码加密64次.这就是BCrypt所回归的.

    重点很简单:这是一个非常昂贵的算法,占用了大量的CPU时间.这是它应该被使用的真正原因.

我希望能够解决问题.