PHP Conference Japan 2024

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_BLOWFISHcrypt() 更適合用於密碼儲存。

參見

新增註解

使用者貢獻的註解 12 個註解

26
clarence.pchy(at)gmail.com
8 年前
請特別注意 **$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"

所以我新增了這個註解。希望它能幫助其他像我一樣的人。
7
does dot not at matter dot org
11 年前
這個程式碼片段一年多前在荷蘭 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: Hash algoritme is niet geinstalleerd op het systeem.');
}

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));
}
}
8
Trevor Herselman
9 年前
這是一個輕量級的替換程式碼,可直接取代 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__ . '(): Unknown hashing algorithm: ' . $algo, E_USER_WARNING);
if (!
is_numeric($count)) trigger_error(__FUNCTION__ . '(): expects parameter 4 to be long, ' . gettype($count) . ' given', E_USER_WARNING);
if (!
is_numeric($length)) trigger_error(__FUNCTION__ . '(): expects parameter 5 to be long, ' . gettype($length) . ' given', E_USER_WARNING);
if (
$count <= 0) trigger_error(__FUNCTION__ . '(): Iterations must be a positive integer: ' . $count, E_USER_WARNING);
if (
$length < 0) trigger_error(__FUNCTION__ . '(): Length must be greater than or equal to 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;
}
}
7
Anonymous
11 年前
可悲的是,這個函式是在 PHP 5.5 中新增的,但許多網路伺服器只提供 PHP 5.3。不過,存在一個純 PHP 實作(在這裡找到:https://defuse.ca/php-pbkdf2.htm)。
我採用了這個實作,將其放入一個帶有 PHPDoc 註解的類別中,並新增了一個切換開關,以便在可用的情況下使用原生的 PHP 函式。

請隨意使用!
http://pastebin.com/f5PDq735
(因為文字太長,所以發佈在 pastebin.com 上)
2
php . ober-mail . de
3 年前
如果您想知道 salt 的要求是什麼,請參閱 RFC[1]

「salt 參數應為包含至少 64 位元熵的隨機字串。這表示當從 *mcrypt_create_iv* 之類的函式產生時,至少要有 8 個位元組長。但對於僅由 *a-zA-Z0-9* 組成(或經過 base64 編碼)的 salt,最小長度應至少為 11 個字元。它應該為每個雜湊的密碼隨機產生,並與產生的金鑰一起儲存。」

[1] https://wiki.php.net/rfc/hash_pbkdf2
1
Yahe
5 年前
在發生錯誤時,hash_pbkdf2() 不僅會引發 E_WARNING,還會傳回 FALSE。
1
nimasdj [AT] yahoo [DOT] com
8 年前
Binod Kumar Luitel 提供的類別中存在錯誤(https://php.dev.org.tw/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);
1
Flimm
6 年前
請注意,如果 $raw_output 為 false,則輸出將使用小寫十六進位字元編碼。某些其他系統(例如 Django 2.0)則改用 base64。因此,如果您嘗試產生與這些系統相容的雜湊字串,可以使用 base64_encode 函式,如下所示

<?php

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

?>
1
gfilippakis at sleed dot gr
5 年前
這是一個 Rfc2898DeriveBytes 類別非常基本的實作,僅包含其 2 個建構函式,以防其他人發現它有用。

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
);
}
}
-1
Binod Kumar Luitel
11 年前
想要純 PHP 函式實作的人,也就是說,伺服器中沒有安裝 PHP 5.5 的人,可以使用以下實作。到目前為止,沒有任何東西從參考 https://defuse.ca/php-pbkdf2.htm 修改過,但喜歡 OOP 的人可能會喜歡這個。
有關 PBKDF2 的更多資訊,請參閱:http://en.wikipedia.org/wiki/PBKDF2

<?php
/**
* 由 RSA 的 PKCS #5 定義的 PBKDF2 金鑰衍生函式:https://www.ietf.org/rfc/rfc2898.txt
* $algorithm - 要使用的雜湊演算法。建議:SHA256
* $password - 密碼。
* $salt - 對於密碼而言是唯一的 salt。
* $count - 迭代計數。越高越好,但也越慢。建議:至少 1000。
* $key_length - 衍生金鑰的長度(以位元組為單位)。
* $raw_output - 如果為 true,則金鑰以原始二進位格式傳回。否則以十六進位編碼。
* 傳回:從密碼和 salt 衍生的 $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;
}
}
}
-2
php at ober-mail dot de
3 年前
如果您想知道 salt 的要求是什麼,請參閱 RFC[1]

「salt 參數應為包含至少 64 位元熵的隨機字串。這表示當從 *mcrypt_create_iv* 之類的函式產生時,至少要有 8 個位元組長。但對於僅由 *a-zA-Z0-9* 組成(或經過 base64 編碼)的 salt,最小長度應至少為 11 個字元。它應該為每個雜湊的密碼隨機產生,並與產生的金鑰一起儲存。」

[1] https://wiki.php.net/rfc/hash_pbkdf2
-3
php - ober-mail - de
3 年前
如果您想知道 salt 的要求是什麼,請參閱 RFC[1]

「salt 參數應為包含至少 64 位元熵的隨機字串。這表示當從 *mcrypt_create_iv* 之類的函式產生時,至少要有 8 個位元組長。但對於僅由 *a-zA-Z0-9* 組成(或經過 base64 編碼)的 salt,最小長度應至少為 11 個字元。它應該為每個雜湊的密碼隨機產生,並與產生的金鑰一起儲存。」

[1] https://wiki.php.net/rfc/hash_pbkdf2
To Top