crypt

(PHP 4, PHP 5, PHP 7, PHP 8)

crypt单向字符串哈希

警告

此函数尚不支持二进制安全!

描述

crypt(#[\SensitiveParameter] string $string, string $salt): string

crypt() 将使用标准的 Unix DES 基于算法或替代算法返回一个哈希字符串。 password_verify()crypt() 兼容。因此,由 crypt() 创建的密码哈希可以与 password_verify() 一起使用。

在 PHP 8.0.0 之前,salt 参数是可选的。但是,crypt() 在没有 salt 的情况下会创建一个弱哈希,并且在没有 salt 的情况下会引发 E_NOTICE 错误。确保为更好的安全性指定一个足够强的盐。

password_hash() 使用强哈希,自动生成强盐,并应用适当的轮数。 password_hash() 是一个简单的 crypt() 包装器,并且与现有的密码哈希兼容。建议使用 password_hash()

哈希类型由盐参数触发。如果未提供盐,PHP 将根据 MD5 crypt() 的可用性自动生成一个标准的两位字符 (DES) 盐或一个十二位字符 (MD5)。PHP 设置一个名为 CRYPT_SALT_LENGTH 的常量,它指示可用哈希允许的最长有效盐。

基于标准 DES 的 crypt() 返回盐作为输出的最初两个字符。它还只使用 string 的前八个字符,因此以相同八个字符开头的更长字符串将生成相同的结果(当使用相同的盐时)。

支持以下哈希类型

  • CRYPT_STD_DES - 标准的基于 DES 的哈希,具有来自字母 "./0-9A-Za-z" 的两个字符的盐。在盐中使用无效字符会导致 crypt() 失败。
  • CRYPT_EXT_DES - 扩展的基于 DES 的哈希。 "salt" 是一个 9 个字符的字符串,由一个下划线后跟 4 个字符的迭代次数和 4 个字符的盐组成。这 4 个字符的字符串中的每一个都编码 24 位,最低有效字符在前。值 063 被编码为 ./0-9A-Za-z。在盐中使用无效字符会导致 crypt() 失败。
  • CRYPT_MD5 - 使用以 $1$ 开头的十二位字符盐的 MD5 哈希
  • CRYPT_BLOWFISH - 使用以下盐的 Blowfish 哈希:"$2a$"、"$2x$" 或 "$2y$"、一个两位数的成本参数、"$" 和来自字母 "./0-9A-Za-z" 的 22 个字符。在盐中使用此范围之外的字符会导致 crypt() 返回一个零长度字符串。两位数的成本参数是底层 Blowfish 基于哈希算法的迭代次数的以 2 为底的对数,必须在 04-31 范围内,此范围之外的值会导致 crypt() 失败。"$2x$" 哈希可能很弱;"$2a$" 哈希兼容,并减轻了这种弱点。对于新的哈希,应该使用 "$2y$"。
  • CRYPT_SHA256 - 带有以 $5$ 为前缀的十六位字符盐的 SHA-256 哈希。如果盐字符串以 'rounds=<N>$' 开头,则 N 的数值用于指示哈希循环应执行的次数,类似于 Blowfish 上的成本参数。默认轮数为 5000,最小值为 1000,最大值为 999,999,999。任何超出此范围的 N 选择都将被截断到最近的限制。
  • CRYPT_SHA512 - 带有以 $6$ 为前缀的十六位字符盐的 SHA-512 哈希。如果盐字符串以 'rounds=<N>$' 开头,则 N 的数值用于指示哈希循环应执行的次数,类似于 Blowfish 上的成本参数。默认轮数为 5000,最小值为 1000,最大值为 999,999,999。任何超出此范围的 N 选择都将被截断到最近的限制。

参数

string

要哈希的字符串。

警告

使用 CRYPT_BLOWFISH 算法,会导致 string 参数被截断到最大长度为 72 字节。

salt

用于作为哈希基础的盐字符串。如果未提供,则行为由算法实现定义,可能导致意外结果。

返回值

返回哈希字符串或长度小于 13 个字符的字符串,并且在失败时保证与盐不同。

警告

验证密码时,应使用不受时序攻击影响的字符串比较函数来比较 crypt() 的输出与以前已知的哈希值。PHP 为此提供了 hash_equals()

变更日志

版本 描述
8.0.0 salt 现在不再是可选的。

示例

示例 #1 crypt() 示例

<?php
$user_input
= 'rasmuslerdorf';
$hashed_password = '$6$rounds=1000000$NJy4rIPjpOaU$0ACEYGg/aKCY3v8O8AfyiO7CTfZQ8/W231Qfh2tRLmfdvFD6XfHk12u6hMr9cYIA4hnpjLNSTRtUwYr9km9Ij/';

// 验证现有 crypt() 哈希,这种方式与非 PHP 软件兼容。
if (hash_equals($hashed_password, crypt($user_input, $hashed_password))) {
echo
"Password verified!";
}
?>

注释

注意: 没有解密函数,因为 crypt() 使用的是单向算法。

参见

  • hash_equals() - 安全抵抗时序攻击的字符串比较
  • password_hash() - 创建密码哈希
  • 有关更多信息,请查看 Unix 手册页中的 crypt 函数。

添加注释

用户贡献注释 7 notes

bob dot orr at mailinator dot com
9 年前
此评论页面上的第 2 条评论(截至 2015 年 2 月)已有 9 年历史,并推荐了 phpass。我已经独立地对该产品进行了安全审计,虽然它仍然被推荐用于密码安全,但它实际上并不安全,不应使用。它多年来没有更新(仍然是 v0.3 版),并且有更新的替代方案,例如使用较新的内置 PHP password_hash() 函数,这些函数要好得多。每个人请花点时间确认我说的是否正确(即自行查看 phpass 代码),然后单击向下箭头将 phpass 评论下沉到底部。这样做将提高整个互联网的安全级别。

对于那些想要了解详细信息的人来说:md5() 和 microtime() 是 phpass 源代码中的一个后备方案。它不会终止执行,而是继续执行代码。作者试图让代码在任何地方都能运行的想法值得称赞,但是,在应用程序安全方面,这种立场实际上适得其反。在安全环境中,唯一正确的答案是终止应用程序,而不是退回到可能被利用的弱势位置(通常是通过强制使用这种较弱的位置)。
Marten Jacobs
10 年前
据我所知,Blowfish 通常被认为是一种安全的哈希算法,即使是企业使用(如果我错了请纠正我)。因此,我创建了使用此算法创建和检查安全密码哈希的函数,并使用(也被认为是密码学安全的)openssl_random_pseudo_bytes 函数来生成盐。

<?php
/*
* 为给定密码生成安全哈希。成本被传递
* 到 Blowfish 算法。查看 PHP 手册页面以获取有关 crypt 的更多信息
* 查找有关此设置的更多信息。
*/
function generate_hash($password, $cost=11){
/* 为了生成盐,首先生成足够多的随机字节。因为
* base64 为每 6 位返回一个字符,因此我们应该生成
* 至少 22*6/8=16.5 字节,所以我们生成 17。然后我们得到第一个
* 22 个 base64 字符
*/
$salt=substr(base64_encode(openssl_random_pseudo_bytes(17)),0,22);
/* 由于 Blowfish 使用字母表 ./A-Za-z0-9 的盐,因此我们必须
* 将 base64 字符串中的任何 '+' 替换为 '.'。我们不必做
* 关于 '=' 的任何事情,因为这只会发生在 b64 字符串是
* 填充,这始终是在前 22 个字符之后。
*/
$salt=str_replace("+",".",$salt);
/* 接下来,创建一个将传递给 crypt 的字符串,其中包含所有
* 设置,用美元符号分隔
*/
$param='$'.implode('$',array(
"2y", // 选择最安全的 Blowfish 版本(>=PHP 5.3.7)
str_pad($cost,2,"0",STR_PAD_LEFT), // 在两位数中添加成本
$salt // 添加盐
));

// 现在进行实际的哈希运算
return crypt($password,$param);
}

/*
* 使用 generate_hash 函数生成的哈希检查密码。
*/
function validate_pw($password, $hash){
/* 使用可用的哈希作为选项参数重新生成,如果传递相同的密码,应该
* 生成相同的哈希。
*/
return crypt($password, $hash)==$hash;
}
?>
kaminski at istori dot com
13 年前
这是一个用于为 CRYPT_BLOWFISH 哈希类型生成伪随机盐的表达式

<?php $salt = substr(str_replace('+', '.', base64_encode(pack('N4', mt_rand(), mt_rand(), mt_rand(), mt_rand()))), 0, 22); ?>

它旨在用于 mt_getrandmax() == 2147483647 的系统。

创建的盐将是 128 位长,填充到 132 位,然后用 22 个 base64 字符表示。(CRYPT_BLOWFISH 仅使用 128 位用于盐,即使 22 个 base64 字符中有 132 位。如果您检查 CRYPT_BLOWFISH 的输入和输出,您会发现它会忽略输入的最后四位,并在输出时将其设置为零。)

请注意,mt_rand() 返回的四个 32 位字的高位始终为零(因为 mt_getrandmax == 2^31),因此只有 128 位中的 124 位是伪随机的。我发现这对于我的应用程序是可以接受的。
steve at tobtu dot com
11 年前
要生成盐,请使用 mcrypt_create_iv() 而不是 mt_rand(),因为无论您调用 mt_rand() 多少次,它都最多只有 32 位熵。当用户数量达到大约 2^16 时,您将开始看到盐冲突。mt_rand() 的种子设置很差,因此这种情况可能会更早发生。

对于 bcrypt,这实际上会生成一个 128 位的盐
<?php $salt = strtr(base64_encode(mcrypt_create_iv(16, MCRYPT_DEV_URANDOM)), '+', '.'); ?>

*** 车库***
22 个字符盐中的最后一个字符是 2 位。
base64_encode() 将有这四个字符“AQgw”
bcrypt 将有这四个字符“。Oeu”

您不需要进行完整的转换,因为它们“四舍五入”到不同的字符
echo crypt('', '$2y$05$.....................A') . "\n";
echo crypt('', '$2y$05$.....................Q') . "\n";
echo crypt('', '$2y$05$.....................g') . "\n";
echo crypt('', '$2y$05$.....................w') . "\n";

$2y$05$......................J2ihDv8vVf7QZ9BsaRrKyqs2tkn55Yq
$2y$05$.....................O/jw2XygQa2.LrIT7CFCBQowLowDP6Y.
$2y$05$.....................eDOx4wMcy7WU.kE21W6nJfdMimsBE3V6
$2y$05$.....................uMMcgjnOELIa6oydRivPkiMrBG8.aFp.
jette at nerdgirl dot dk
11 年前
crypt() 函数无法正确处理加号。因此,例如,如果您在登录函数中使用 crypt,请首先对密码使用 urlencode 以确保登录过程可以处理任何字符

<?php
$user_input
= '12+#æ345';
$pass = urlencode($user_input));
$pass_crypt = crypt($pass);

if (
$pass_crypt == crypt($pass, $pass_crypt)) {
echo
"Success! Valid password";
} else {
echo
"Invalid password";
}
?>
Anonymous
6 年前
steve at tobtu dot com 在 4 年前是正确的,但现在 mcrypt_create_iv()(以及 bcrypt 本身)已被弃用!

改用 random_bytes()

<?php
$salt
= base64_encode(random_bytes(16));
Joey
6 年前
虽然文档说如果盐无效,crypt 将无法处理 DES,但事实并非如此。

只要 crypt 函数不会匹配任何其他哈希模式,它就会接受任何两个或更多个字符的字符串作为 DES。其余字符将被忽略。
To Top