hash_pbkdf2

(PHP 5 >= 5.5.0, PHP 7, PHP 8)

hash_pbkdf2生成提供的密码的 PBKDF2 密钥派生

描述

hash_pbkdf2(
    字符串 $algo,
    #[\SensitiveParameter] 字符串 $password,
    字符串 $salt,
    整数 $iterations,
    整数 $length = 0,
    布尔 $binary = false,
    数组 $options = []
): 字符串

参数

algo

所选哈希算法的名称(例如 "sha256")。有关支持算法的列表,请参见 hash_hmac_algos().

注意:

不允许使用非加密哈希函数。

password

用于派生的密码。

salt

用于派生的盐。此值应随机生成。

iterations

为派生执行的内部迭代次数。

length

输出字符串的长度。如果 binarytrue,则对应于派生密钥的字节长度,如果 binaryfalse,则对应于派生密钥的字节长度的两倍(因为密钥的每个字节都以两个十六进制数的形式返回)。

如果传递 0,则使用所提供算法的整个输出。

binary

设置为 true 时,输出原始二进制数据。false 输出小写十六进制数。

options

各种哈希算法的选项数组。目前,只有 MurmurHash 变体支持 "seed" 键。

返回值

返回一个字符串,其中包含派生密钥,以小写十六进制数表示,除非 binary 设置为 true,在这种情况下,将返回派生密钥的原始二进制表示形式。

错误/异常

如果算法未知,iterations 参数小于或等于 0length 小于 0salt 太长(大于 INT_MAX - 4),则抛出 ValueError 异常。

变更日志

版本 描述
8.0.0 现在在发生错误时抛出 ValueError 异常。以前,返回 false 并且会发出 E_WARNING 消息。
7.2.0 禁用了非加密哈希函数(adler32、crc32、crc32b、fnv132、fnv1a32、fnv164、fnv1a64、joaat)的使用。

示例

示例 #1 hash_pbkdf2() 示例,基本用法

<?php
$password
= "password";
$iterations = 600000;

// 使用 random_bytes() 生成密码学安全的随机盐
$salt = random_bytes(16);

$hash = hash_pbkdf2("sha256", $password, $salt, $iterations, 20);
var_dump($hash);

// 对于原始二进制,$length 需要减半以获得等效结果
$hash = hash_pbkdf2("sha256", $password, $salt, $iterations, 10, true);
var_dump(bin2hex($hash));?>

上面的示例将输出类似以下内容

string(20) "120fb6cffcf8b32c43e7"
string(20) "120fb6cffcf8b32c43e7"

注意

警告

PBKDF2 方法可用于对密码进行哈希处理以进行存储。但是,需要注意的是,password_hash()crypt()CRYPT_BLOWFISH 更适合密码存储。

参见

添加备注

用户贡献笔记 13 笔记

25
clarence.pchy(at)gmail.com
7 年前
请特别注意 **$length** 参数!它正是 **返回字符串的长度**,而不是原始二进制哈希结果的长度。

我在这个问题上遇到过很大的麻烦——
我认为 `hash_pbkdf2(...false)` 应该等于 `bin2hex(hash_pbkdf2(...true))`,就像 `md5($x)` 等于 `bin2hex(md5($x, true))` 一样。然而我错了。

hash_pbkdf2('sha256', '123456', 'abc', 10000, 50, false); // 返回字符串(50) "584bc5b41005169f1fa15177edb78d75f9846afc466a4bae05"
hash_pbkdf2('sha256', '123456', 'abc', 10000, 50, true); // 返回字符串(50) "XKŴ � �Qw�u��j�FjK� ��BFW� YpG �mp.g2�`; N�"
bin2hex(hash_pbkdf2('sha256', '123456', 'abc', 10000, 50, true)); // 返回字符串(100) "584bc5b41005169f1fa15177edb78d75f9846afc466a4bae05119c82424657c81b5970471f098a6d702e6732b7603b194efe"

所以我添加了这样的注释。希望它能帮助像我一样的其他人。
8
does dot not at matter dot org
10 年前
这段代码片段在一年前发布在一个荷兰 PHP 社区上:(参考/来源:http://www.phphulp.nl/php/script/beveiliging/pbkdf2-een-veilige-manier-om-wachtwoorden-op-te-slaan/1956/pbkdf2php/1757/)

<?php

/**
* @author Chris Horeweg
* @package Security_Tools
*/

function pbkdf2($password, $salt, $algorithm = 'sha512', $count = 20000, $key_length = 128, $raw_output = false)
{
if(!
in_array($algorithm, hash_algos(), true)) {
exit(
'pbkdf2: 哈希算法未在系统上安装.');
}

if(
$count <= 0 || $key_length <= 0) {
$count = 20000;
$key_length = 128;
}

$hash_length = strlen(hash($algorithm, "", true));
$block_count = ceil($key_length / $hash_length);

$output = "";
for(
$i = 1; $i <= $block_count; $i++) {
$last = $salt . pack("N", $i);
$last = $xorsum = hash_hmac($algorithm, $last, $password, true);
for (
$j = 1; $j < $count; $j++) {
$xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
}
$output .= $xorsum;
}

if(
$raw_output) {
return
substr($output, 0, $key_length);
}
else {
return
base64_encode(substr($output, 0, $key_length));
}
}
9
Trevor Herselman
8 年前
这是 PHP 的 hash_pbkdf2(); 的轻量级直接替换,专为与旧版本的 PHP 兼容而编写。
由我自己编写、格式化和测试,但使用以下代码和想法
https://defuse.ca/php-pbkdf2.htm
https://github.com/rchouinard/hash_pbkdf2-compat/blob/master/src/hash_pbkdf2.php
https://gist.github.com/rsky/5104756

我的主要目标
1) 与 PHP hash_pbkdf2() 最大程度兼容,即直接替换函数
2) 最小代码大小/膨胀
3) 易于复制/粘贴
4) 没有类,也不封装在类中!为什么写一个类,当一个简单的函数就能做到的时候?
5) 消除对 sprintf() 的调用。(在其他示例中用于错误报告)
6) 没有其他依赖项,即额外的必需函数

<?php
if (!function_exists('hash_pbkdf2'))
{
function
hash_pbkdf2($algo, $password, $salt, $count, $length = 0, $raw_output = false)
{
if (!
in_array(strtolower($algo), hash_algos())) trigger_error(__FUNCTION__ . '(): 未知哈希算法: ' . $algo, E_USER_WARNING);
if (!
is_numeric($count)) trigger_error(__FUNCTION__ . '(): 预期参数 4 为长整型,' . gettype($count) . ' 给定', E_USER_WARNING);
if (!
is_numeric($length)) trigger_error(__FUNCTION__ . '(): 预期参数 5 为长整型,' . gettype($length) . ' 给定', E_USER_WARNING);
if (
$count <= 0) trigger_error(__FUNCTION__ . '(): 迭代次数必须为正整数: ' . $count, E_USER_WARNING);
if (
$length < 0) trigger_error(__FUNCTION__ . '(): 长度必须大于或等于 0: ' . $length, E_USER_WARNING);

$output = '';
$block_count = $length ? ceil($length / strlen(hash($algo, '', $raw_output))) : 1;
for (
$i = 1; $i <= $block_count; $i++)
{
$last = $xorsum = hash_hmac($algo, $salt . pack('N', $i), $password, true);
for (
$j = 1; $j < $count; $j++)
{
$xorsum ^= ($last = hash_hmac($algo, $last, $password, true));
}
$output .= $xorsum;
}

if (!
$raw_output) $output = bin2hex($output);
return
$length ? substr($output, 0, $length) : $output;
}
}
8
匿名
11 年前
遗憾的是,此函数在 PHP 5.5 中添加,但许多 Web 服务器只提供 PHP 5.3。但存在纯 PHP 实现(在此处找到:https://defuse.ca/php-pbkdf2.htm)。
我采用了这种实现,将其放入一个类中,并添加了 PHPDoc 的注释,还添加了一个开关,以便在可用时使用本机 PHP 函数。

请随意使用它!
http://pastebin.com/f5PDq735
(发布在 pastebin.com 上,因为文本太长了)
2
Flimm
6 年前
请注意,如果 $raw_output 为 false,则输出将使用小写十六进制编码。其他一些系统(如 Django 2.0)使用 base64。因此,如果您尝试生成与这些系统兼容的哈希字符串,可以使用 base64_encode 函数,如下所示

<?php

echo base64_encode( hash_pbkdf2( "sha256", "example password", "BbirbJq1C1G7", 100000, 0, true ) );

?>
2
gfilippakis at sleed dot gr
5 年前
这是一个非常基本的 Rfc2898DeriveBytes 类实现,只包含它的两个构造函数,以备不时之需。

class Rfc2898DeriveBytes
{
private $textToHash;
private $saltByteSize;

public $salt;

public function __construct($arg1, $arg2)
{
if (is_string($arg1) && is_integer($arg2)) {
$this->textToHash = $arg1;
$this->saltByteSize = $arg2;
$this->salt = substr(
hex2bin(sha1(uniqid('', true))),
0,
$this->saltByteSize
);
} elseif (is_string($arg1) && is_string($arg2)) {
$this->textToHash = $arg1;
$this->salt = $arg2;
}
}

public function getBytes($size)
{
return hash_pbkdf2(
"sha1",
$this->textToHash,
$this->salt,
1000,
$size,
true
);
}
}
2
php . ober-mail . de
3 年前
如果您想知道对盐的要求是什么,请查看 RFC[1]

"盐参数应该是一个随机字符串,包含至少 64 位的熵。这意味着当从像 *mcrypt_create_iv* 这样的函数生成时,至少 8 字节长。但对于只包含 *a-zA-Z0-9*(或经过 base_64 编码)的盐,最小长度应至少为 11 个字符。它应该为要散列的每个密码随机生成,并与生成的密钥一起存储."

[1] https://wiki.php.net/rfc/hash_pbkdf2
2
Yahe
4 年前
如果出现错误,`hash_pbkdf2()` 不会仅仅抛出 `E_WARNING`,还会返回 `FALSE`。
0
php at ober-mail dot de
3 年前
如果您想知道对盐的要求是什么,请查看 RFC[1]

"盐参数应该是一个随机字符串,包含至少 64 位的熵。这意味着当从像 *mcrypt_create_iv* 这样的函数生成时,至少 8 字节长。但对于只包含 *a-zA-Z0-9*(或经过 base_64 编码)的盐,最小长度应至少为 11 个字符。它应该为要散列的每个密码随机生成,并与生成的密钥一起存储."

[1] https://wiki.php.net/rfc/hash_pbkdf2
0
nimasdj [AT] yahoo [DOT] com
8 年前
Binod Kumar Luitel 提供的类中存在错误 (https://php.net/manual/en/function.hash-pbkdf2.php#113488)
以下这行代码
return bin2hex(substr($this->output, 0, $this->key_length));
需要修改为
return substr(bin2hex($this->output), 0, $this->key_length);
0
Binod Kumar Luitel
10 年前
对于想要使用纯 PHP 实现该函数的用户,即服务器上未安装 PHP 5.5 的用户,可以使用以下实现。代码未进行任何修改,参考了 https://defuse.ca/php-pbkdf2.htm,但 OOP 爱好者可能会喜欢这种实现。
有关 PBKDF2 的更多信息,请参阅:http://en.wikipedia.org/wiki/PBKDF2

<?php
/**
* PBKDF2 密钥推导函数,如 RSA 的 PKCS #5 中所定义:https://www.ietf.org/rfc/rfc2898.txt
* $algorithm - 要使用的哈希算法。推荐:SHA256
* $password - 密码。
* $salt - 密码独有的盐。
* $count - 迭代次数。越高越好,但速度越慢。推荐:至少 1000。
* $key_length - 推导出的密钥的字节长度。
* $raw_output - 如果为 true,则密钥以原始二进制格式返回。否则以十六进制编码返回。
* 返回:从密码和盐中推导出的 $key_length 字节密钥。
*/
if (!function_exists("hash_pbkdf2")) {
function
hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false) {

class
pbkdf2 {
public
$algorithm;
public
$password;
public
$salt;
public
$count;
public
$key_length;
public
$raw_output;

private
$hash_length;
private
$output = "";

public function
__construct($data = null)
{
if (
$data != null) {
$this->init($data);
}
}

public function
init($data)
{
$this->algorithm = $data["algorithm"];
$this->password = $data["password"];
$this->salt = $data["salt"];
$this->count = $data["count"];
$this->key_length = $data["key_length"];
$this->raw_output = $data["raw_output"];
}

public function
hash()
{
$this->algorithm = strtolower($this->algorithm);
if(!
in_array($this->algorithm, hash_algos(), true))
throw new
Exception('PBKDF2 ERROR: Invalid hash algorithm.');

if(
$this->count <= 0 || $this->key_length <= 0)
throw new
Exception('PBKDF2 ERROR: Invalid parameters.');

$this->hash_length = strlen(hash($this->algorithm, "", true));
$block_count = ceil($this->key_length / $this->hash_length);
for (
$i = 1; $i <= $block_count; $i++) {
// $i 编码为 4 个字节,大端序。
$last = $this->salt . pack("N", $i);
// 第一次迭代
$last = $xorsum = hash_hmac($this->algorithm, $last, $this->password, true);
// 执行其他 $this->count - 1 次迭代
for ($j = 1; $j < $this->count; $j++) {
$xorsum ^= ($last = hash_hmac($this->algorithm, $last, $this->password, true));
}
$this->output .= $xorsum;
if(
$this->raw_output)
return
substr($this->output, 0, $this->key_length);
else
return
bin2hex(substr($this->output, 0, $this->key_length));
}
}
}

$data = array('algorithm' => $algorithm, 'password' => $password, 'salt' => $salt, 'count' => $count, 'key_length' => $key_length, 'raw_output' => $raw_output);
try {
$pbkdf2 = new pbkdf2($data);
return
$pbkdf2->hash();
} catch (
Exception $e) {
throw
$e;
}
}
}
-3
php - ober-mail - de
3 年前
如果您想知道对盐的要求是什么,请查看 RFC[1]

"盐参数应该是一个随机字符串,包含至少 64 位的熵。这意味着当从像 *mcrypt_create_iv* 这样的函数生成时,至少 8 字节长。但对于只包含 *a-zA-Z0-9*(或经过 base_64 编码)的盐,最小长度应至少为 11 个字符。它应该为要散列的每个密码随机生成,并与生成的密钥一起存储."

[1] https://wiki.php.net/rfc/hash_pbkdf2
-9
Peter
10 年前
另请参阅 https://github.com/rchouinard/hash_pbkdf2-compat 以了解兼容性函数。
To Top