在PHP中使用crypt()比较密码

时间:2010-06-28 19:31:31

标签: php encryption bcrypt

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

  

Blowfish用盐进行散列如下:“$ 2a $”,两位数的成本参数“$”,以及字母“./0-9A-Za-z”中的22位64位数字。在salt 中使用此范围之外的字符将导致crypt()返回零长度字符串

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

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

然而,它吐了出来:

$2a$07$usesomadasdsadsadsadaeMTUHlZEItvtV00u0.kb7qhDlC0Kou9e

看来crypt()已将盐本身切成22的长度。有人可以解释一下吗?

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

$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?
}    

感谢您的时间

6 个答案:

答案 0 :(得分:21)

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

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

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

将整个$ digest存储在数据库中,它同时包含salt和digest。

比较密码时,只需执行此操作,

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

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

答案 1 :(得分:3)

每个密码的新盐

$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

答案 2 :(得分:2)

从手册中引用

  

CRYPT_BLOWFISH - 与河豚一起散列   盐如下:“$ 2a $”,两位数   成本参数,“$”和22基数64   字母表中的数字

注意: 22 基本64位

答案 3 :(得分:2)

BCrypt对盐使用128位,因此使用22字节Base64,只使用最后一个字节的两位。

使用salt和密码计算哈希值。当您传递加密密码时,算法将读取强度,盐(忽略除此之外的所有内容)和您提供的密码,并计算哈希值,并附加它。如果你有方便的PostgreSQL和pg_crypto,SELECT gen_salt('bf');会告诉你正在读什么盐。

这是一个用于生成盐的代码示例,来自我的.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函数。

答案 4 :(得分:0)

第一个问题:

  

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

     

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

     

crypt()似乎已经削减了   盐本身长度为22。   有人可以解释一下吗?

有太多字符没有问题......短语在salt中使用此范围之外的字符将导致crypt()返回零长度字符串指向外部 base 64 的范围不是 22个字符的范围。尝试在salt字符串中放入一个非法字符,你会发现你得到一个空输出(或者你输入了<22个字符,导致非法的空字节)。

第二个问题:

您将加密的存储密码作为salt传递,因为salt字符串始终(通过设计)出现在加密字符串中,这样您就可以确保对存储密码和用户输入密码的加密具有相同的盐。 / p>

答案 5 :(得分:0)

这个问题与我对ZZ Coder答案的回应有关。基本上我的问题是关于将crypt()结果存储在数据库中。我应该将整个输出存储在数据库中,以便我的数据库看起来像这样:

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

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

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