17

我需要了解此功能的基础知识。对于河豚算法,php.net 文档指出:

使用盐的河豚散列如下:“$2a$”,两位数的成本参数,“$”,以及字母表“./0-9A-Za-z”中的 22 个基数为 64 位的数字。在盐中使用超出此范围的字符将导致 crypt() 返回零长度字符串

因此,根据定义,这不应该起作用:

echo crypt('rasmuslerdorf', '$2a$07$usesomadasdsadsadsadasdasdasdsadesillystringforsalt$');

然而,它吐出:

$2a$07$usesomadasdsadsadsadaeMTUHlZEItvtV00u0.kb7qhDlC0Kou9e

似乎 crypt() 已将盐本身的长度减少到 22。有人可以解释一下吗?

这个函数的另一个我无法理解的方面是当他们使用 crypt() 来比较密码时。http://php.net/manual/en/function.crypt.php(查看 ex.#1)。这是否意味着如果我使用相同的盐来加密所有密码,我必须先加密它?IE:

$salt = "usesomadasdsadsadsadae";
$salt_crypt = crypt($salt);

if (crypt($user_input, $salt) == $password) {
   // FAIL WONT WORK
}

if (crypt($user_input, $salt_crypt) == $password) {
   // I HAVE TO DO THIS?
}    

谢谢你的时间

4

6 回答 6

22

以下代码示例可能会回答您的问题。

要使用 Blowfish 生成散列密码,首先需要生成一个 salt,它以 $2a$ 开头,后跟迭代计数和 22 个字符的 Base64 字符串。

$salt = '$2a$07$usesomadasdsadsadsadasdasdasdsadesillystringfors';
$digest = crypt('rasmuslerdorf', $salt);

将整个 $digest 存储在数据库中,它同时具有盐和摘要。

比较密码时,只需这样做,

  if (crypt($user_input, $digest) == $digest)

您正在将摘要用作盐。crypt 知道算法标识符中的盐有多长。

于 2010-06-28T21:16:22.673 回答
4

每个密码的新盐

$password = 'p@ssw0rd';

$salt = uniqid('', true);
$algo = '6'; // CRYPT_SHA512
$rounds = '5042';
$cryptSalt = '$'.$algo.'$rounds='.$rounds.'$'.$salt;

$hashedPassword = crypt($password, $cryptSalt);
// Store complete $hashedPassword in DB

echo "<hr>$password<hr>$algo<hr>$rounds<hr>$cryptSalt<hr>$hashedPassword";

验证

if (crypt($passwordFromPost, $hashedPasswordInDb) == $hashedPasswordInDb) {
    // Authenticated
于 2013-03-27T13:07:11.683 回答
2

从手册中引用

CRYPT_BLOWFISH - 使用盐的 Blowfish 散列如下:“$2a$”,一个两位数的成本参数,“$”,以及 22 个基数为 64 位的字母表

注:22基数 64 位

于 2010-06-28T19:44:39.207 回答
2

BCrypt 使用 128 位作为盐,因此是 22 字节 Base64,最后一个字节只使用了两位。

哈希是使用盐和密码计算的。当您传递加密密码时,算法会读取强度、盐(忽略它之外的所有内容)和您提供的密码,并计算散列并附加它。如果你手边有 PostgreSQL 和 pg_crypto, SELECT gen_salt('bf'); 将向您展示正在读取的 $salt 的内容。

这是盐生成的代码示例,来自我的.NET implementation的 test-vector-gen.php,或者:

$salt = sprintf('$2a$%02d$%s', [strength goes here],
    strtr(str_replace(
    '=', '', base64_encode(openssl_random_pseudo_bytes(16))
    ),
    'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
    './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'));

没有理由对所有密码使用相同的盐。无论如何,盐是输出的一部分,因此您在方便时一无所获……尽管我承认 PHP 应该具有内置的 gen_salt 函数。

于 2011-01-22T17:08:08.423 回答
0

第一个问题:

因此,根据定义,这不应该起作用:

echo crypt('rasmuslerdorf', '$2a$07$usesomadasdsadsadsadasdasdasdsadesillystringforsalt$');

似乎 crypt() 已将盐本身的长度减少到 22。有人可以解释一下吗?

有太多字符没有问题......短语在盐中使用超出此范围的字符将导致 crypt() 返回一个零长度字符串,指的是基数 64范围之外而不是22范围字符。尝试在 salt 字符串中放入非法字符,您应该会发现输出为空(或者如果放入 < 22 个字符,则会导致非法空字节)。

第二个问题:

您将加密的存储密码作为 salt 传递,因为 salt 字符串总是(按设计)出现在加密字符串中,这样您就可以确保对存储的密码和用户输入的密码进行加密时使用相同的 salt。

于 2010-06-28T19:47:54.523 回答
0

这个问题与我对 ZZ Coder 的回答有关。基本上我的问题是关于将 crypt() 结果存储在数据库中。我是否应该将整个输出存储在数据库中,以便我的数据库如下所示:

--------------------------------------------------------------------------------
| ID | Username |                          Password                            |
--------------------------------------------------------------------------------
| 32 | testuser | $2a$07$usesomadasdsadsadsadaeMTUHlZEItvtV00u0.kb7qhDlC0Kou9e |
--------------------------------------------------------------------------------

如果是,那么这不是违背了使用盐的目的吗?如果有人可以访问数据库,他们可以清楚地看到用于加密的盐吗?

额外问题:为每个密码使用相同的盐是否安全?

于 2010-06-29T20:54:03.140 回答