PHPのお勉強!

PHP TOP

hash_pbkdf2

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

hash_pbkdf2PBKDF2 を使ってパスワードからキーを生成する

説明

hash_pbkdf2(
    string $algo,
    #[\SensitiveParameter] string $password,
    string $salt,
    int $iterations,
    int $length = 0,
    bool $binary = false,
    array $options = []
): string

パラメータ

algo

ハッシュアルゴリズムの名前 (例: "sha256")。 サポートされるアルゴリズムの一覧は hash_hmac_algos() をご参照ください。

注意:

暗号に適さないハッシュ関数は許可されません。

password

導出に使うパスワード

salt

導出に使うソルト。ランダムに生成した値でなければいけません。

iterations

導出の実行の際の内部の反復回数

length

出力する文字列の長さ。binarytrue の場合、これは導出鍵のバイト長になります。 binaryfalse の場合、 これは導出鍵のバイト長の二倍になります (キーの全バイトが十六進二桁で返されるからです)。

0が渡された場合、与えられたアルゴリズムの出力全体が使われます。

binary

true にセットされている場合、生のバイナリデータが出力されます。false の場合、小文字の16進数が出力されます。

options

様々なハッシュアルゴリズム向けの、オプションの配列。 現状は、MurmurHash バリアントが "seed" だけをサポートしています。

戻り値

小文字の16進数を含む文字列が返されます。binarytrue の場合、導出鍵の生のバイナリ表現が返されます。

エラー / 例外

アルゴリズムが未知である場合、 iterations パラメータが 0 以下である場合、 length0 よりも小さい場合、 salt が長すぎる場合 (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;

// Generate a cryptographically secure random salt using random_bytes()
$salt = random_bytes(16);

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

// for raw binary, the $length needs to be halved for equivalent results
$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 を使うのほうが、パスワードの保存に適しています。

参考

  • password_hash() - パスワードハッシュを作る
  • hash_hkdf() - 与えられたキーから導出されるHKDFキーを生成する
  • sodium_crypto_pwhash() - Argon2 アルゴリズムを使い、パスワードからキーを導出する

add a note

User Contributed Notes 12 notes

up
26
clarence.pchy(at)gmail.com
8 years ago
Please pay great attention to the **$length** parameter! It is exactly the **return string length**, NOT the length of raw binary hash result.

I had a big problem about this --
I thought that `hash_pbkdf2(...false)` should equals to `bin2hex(hash_pbkdf2(...true))` just like `md5($x)` equals `bin2hex(md5($x, true))`. However I was wrong:

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

So I add such a note. Hope it will help someone else like me.
up
7
does dot not at matter dot org
11 years ago
this snippet was posted over a year ago on a dutch PHP community: (reference/source: 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));
}
}
up
8
Trevor Herselman
9 years ago
This is a light-weight drop-in replacement for PHP's hash_pbkdf2(); written for compatibility with older versions of PHP.
Written, formatted and tested by myself, but using code and ideas based on the following:
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

My main goals:
1) Maximum compatibility with PHP hash_pbkdf2(), ie. a drop-in replacement function
2) Minimum code size/bloat
3) Easy to copy/paste
4) No classes, and not encapsulated in a class! Why write a class when a simple function will do?
5) Eliminate calls to sprintf(). (used by other examples for the error reporting)
6) No other dependencies, ie. extra required functions

<?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;
}
}
up
7
Anonymous
11 years ago
Sadly this function was added in PHP 5.5 but many webservers just provide PHP 5.3. But there exists a pure PHP implementation (found here: https://defuse.ca/php-pbkdf2.htm).
I took this implementation, put it into a class with comments for PHPDoc and added a switch so that the native PHP function is used if available.

Feel free to use it!
http://pastebin.com/f5PDq735
(Posted on pastebin.com since the text would have been too long)
up
2
php . ober-mail . de
3 years ago
If you are wondering what the requirements are for the salt, have a look at the RFC[1]:

"The salt parameter should be a random string containing at least 64 bits of entropy. That means when generated from a function like *mcrypt_create_iv*, at least 8 bytes long. But for salts that consist of only *a-zA-Z0-9* (or are base_64 encoded), the minimum length should be at least 11 characters. It should be generated random for each password that's hashed, and stored along side the generated key."

[1] https://wiki.php.net/rfc/hash_pbkdf2
up
1
Yahe
5 years ago
On an error hash_pbkdf2() will not just raise an E_WARNING but it will also return FALSE.
up
1
nimasdj [AT] yahoo [DOT] com
8 years ago
There is a mistake in the class provided by Binod Kumar Luitel (http://php.net/manual/en/function.hash-pbkdf2.php#113488):
this line:
return bin2hex(substr($this->output, 0, $this->key_length));
must be changed to:
return substr(bin2hex($this->output), 0, $this->key_length);
up
1
Flimm
6 years ago
Note that if $raw_output is false, then the output will be encoded using lowercase hexits. Some other systems (such as Django 2.0) use base64 instead. So if you're trying to generate hash strings that are compatible with those systems, you can use the base64_encode function, like this:

<?php

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

?>
up
1
gfilippakis at sleed dot gr
5 years ago
This is a very basic implementation of Rfc2898DeriveBytes class with only 2 of its constructors in case someone else finds it useful.

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
);
}
}
up
-1
Binod Kumar Luitel
11 years ago
People who wants pure PHP implementation of the function, i.e. who don't have PHP 5.5 installed within their server, can use the following implementation. Nothing has been modified so far as from reference https://defuse.ca/php-pbkdf2.htm but the OOP lovers might like this.
For more information about PBKDF2 see: http://en.wikipedia.org/wiki/PBKDF2

<?php
/**
* PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
* $algorithm - The hash algorithm to use. Recommended: SHA256
* $password - The password.
* $salt - A salt that is unique to the password.
* $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.
* $key_length - The length of the derived key in bytes.
* $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
* Returns: A $key_length-byte key derived from the password and salt.
*/
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 encoded as 4 bytes, big endian.
$last = $this->salt . pack("N", $i);
// first iteration
$last = $xorsum = hash_hmac($this->algorithm, $last, $this->password, true);
// perform the other $this->count - 1 iterations
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;
}
}
}
up
-2
php at ober-mail dot de
3 years ago
If you are wondering what the requirements are for the salt, have a look at the RFC[1]:

"The salt parameter should be a random string containing at least 64 bits of entropy. That means when generated from a function like *mcrypt_create_iv*, at least 8 bytes long. But for salts that consist of only *a-zA-Z0-9* (or are base_64 encoded), the minimum length should be at least 11 characters. It should be generated random for each password that's hashed, and stored along side the generated key."

[1] https://wiki.php.net/rfc/hash_pbkdf2
up
-3
php - ober-mail - de
3 years ago
If you are wondering what the requirements are for the salt, have a look at the RFC[1]:

"The salt parameter should be a random string containing at least 64 bits of entropy. That means when generated from a function like *mcrypt_create_iv*, at least 8 bytes long. But for salts that consist of only *a-zA-Z0-9* (or are base_64 encoded), the minimum length should be at least 11 characters. It should be generated random for each password that's hashed, and stored along side the generated key."

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