如何在PHP中使用Blowfish创建和存储密码哈希

2022-08-30 16:13:52

1)如何使用crypt()创建安全的密码河豚哈希?

$hash = crypt('somePassword', '$2a$07$nGYCCmhrzjrgdcxjH$');

1a) “$2a”的意义是什么?它是否只是表明应该使用河豚算法?
1b) “$07”有什么意义?值越高是否意味着哈希值越安全?
1c) “$nGYCCmhrzjrgdcxjH$”有什么意义?这是将要使用的盐吗?这应该是随机生成的吗?硬编码?

2)你如何存储河豚哈希?

echo $hash;
//Output: $2a$07$nGYCCmhrzjrgdcxjH$$$$.xLJMTJxaRa12DnhpAJmKQw.NXXZHgyq

2a)其中的哪一部分应该存储在数据库中?
2b) 列应该使用什么数据类型 (MySQL)?

3)如何验证登录尝试?


答案 1

您应该存储crypt的整个输出,将其拆分没有多大意义,因为在任何情况下都需要为要散列的每个密码生成一个新的盐。使用Matt提到的固定隐藏盐是错误的 - 每个哈希的盐应该不同。

有关更多信息,请参阅 http://www.openwall.com/articles/PHP-Users-Passwords - 我建议使用phpass库,因为它可以为您处理生成随机盐,这与crypt()不同。


答案 2

1a) 加密强度 - 要求在 4..31 范围内。查看 http://php.net/manual/en/function.crypt.php

1b) 参见 1a

“salt”不应该是随机的,否则您将无法为给定的输入重新生成相同的哈希 - 请参阅3。

2a)严格来说,除了哈希之外的所有内容(以防数据库受到损害)。此外,将盐存储在Web服务器的文档根目录下无法访问的文件中,并将其包括在内。使用最严格的权限设置它;理想情况下,只读到Web主机服务(例如apache),没有写入或执行权限。不那么严格地说,这取决于您希望对黑客的防御程度。不储存盐只会使生活更加困难;他们仍然必须正确地将数据输入到算法中 - 但为什么要让它变得更容易呢?

2b) VARCHAR(32) 应该适合河豚,如果不存储哈希值

3)假设您已经运行了正确的注入预防代码等。所以请不要盲目地复制以下内容(理想情况下使用PDO而不是mysql扩展)。以下是针对河豚,SHA-256和SHA-512,它们都返回哈希内的盐。需要修改其他算法...

//store this in another file outside web directory and include it
$salt = '$2a$07$somevalidbutrandomchars$'

...

//combine username + password to give algorithm more chars to work with
$password_hash = crypt($valid_username . $valid_password, $salt)

//Anything less than 13 chars is a failure (see manual)
if (strlen($password_hash) < 13 || $password_hash == $salt)
then die('Invalid blowfish result');

//Drop the salt from beginning of the hash result. 
//Note, irrespective of number of chars provided, algorithm will always 
//use the number defined in constant CRYPT_SALT_LENGTH
$trimmed_password_hash = substring($password_hash, CRYPT_SALT_LENGTH);
mysql_query("INSERT INTO `users` (username,p assword_hash) VALUES '$valid_username', '$trimmed_password_hash'");

...

$dbRes = mysql_query("SELECT password_hash FROM `users` WHERE username = '$user_input_username' LIMIT 1");
//re-apply salt to output of database and re-run algorithm testing for match
if (substring($salt, CRYPT_SALT_LENGTH) . mysql_result($dbRes, 0, 'password_hash') ) ===
        crypt($user_input_username . $user_input_password, $salt) ) {
    //... do stuff for validated user
}

推荐