2012-10-07 135 views
0

感謝在最近發佈的一些很好的建議,我正在實施PBKDF2從 https://defuse.ca/php-pbkdf2.htm到一個小的PHP圖像庫我正在建立自學一些PHP。PBKDF2 PHP的密碼散列

我知道你將salt和hash存儲在數據庫中,然後重建它們以便在用戶輸入密碼時匹配。我不明白的是,當上面的網站的validate_password函數爲相同的密碼生成不同的唯一salt時,它的工作原理如何。

例如,我創建了一個反覆生成只是我的名字(安德魯)密碼的測試。每次最後的哈希值都不相同,大概是由於鹽是獨特的?然而,哪一個生成的散列並不重要,我也比較了密碼,它驗證了它。 (它顯然沒有驗證完全不同的密碼,如Andrew56或ndrew1)。

任何人都可以向我簡單的自我解釋爲什麼是這樣嗎?好像我甚至不需要儲存鹽?我希望這不是主題。

<?php 
/* 
* Password hashing with PBKDF2. 
* Author: havoc AT defuse.ca 
* www: https://defuse.ca/php-pbkdf2.htm 
*/ 

// These constants may be changed without breaking existing hashes. 
define("PBKDF2_HASH_ALGORITHM", "sha256"); 
define("PBKDF2_ITERATIONS", 1000); 
define("PBKDF2_SALT_BYTES", 24); 
define("PBKDF2_HASH_BYTES", 24); 

define("HASH_SECTIONS", 4); 
define("HASH_ALGORITHM_INDEX", 0); 
define("HASH_ITERATION_INDEX", 1); 
define("HASH_SALT_INDEX", 2); 
define("HASH_PBKDF2_INDEX", 3); 

function create_hash($password) 
{ 
// format: algorithm:iterations:salt:hash 
$salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTES, MCRYPT_DEV_URANDOM)); 
return PBKDF2_HASH_ALGORITHM . ":" . PBKDF2_ITERATIONS . ":" . $salt . ":" . 
    base64_encode(pbkdf2(
     PBKDF2_HASH_ALGORITHM, 
     $password, 
     $salt, 
     PBKDF2_ITERATIONS, 
     PBKDF2_HASH_BYTES, 
     true 
    )); 
} 

function validate_password($password, $good_hash) 
{ 
$params = explode(":", $good_hash); 
if(count($params) < HASH_SECTIONS) 
    return false; 
$pbkdf2 = base64_decode($params[HASH_PBKDF2_INDEX]); 
return slow_equals(
    $pbkdf2, 
    pbkdf2(
     $params[HASH_ALGORITHM_INDEX], 
     $password, 
     $params[HASH_SALT_INDEX], 
     (int)$params[HASH_ITERATION_INDEX], 
     strlen($pbkdf2), 
     true 
    ) 
); 
} 

// Compares two strings $a and $b in length-constant time. 
function slow_equals($a, $b) 
{ 
$diff = strlen($a)^strlen($b); 
for($i = 0; $i < strlen($a) && $i < strlen($b); $i++) 
{ 
    $diff |= ord($a[$i])^ord($b[$i]); 
} 
return $diff === 0; 
} 

/* 
* 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. 
* 
* Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt 
* 
* This implementation of PBKDF2 was originally created by https://defuse.ca 
* With improvements by http://www.variations-of-shadow.com 
*/ 
function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false) 
{ 
$algorithm = strtolower($algorithm); 
if(!in_array($algorithm, hash_algos(), true)) 
    die('PBKDF2 ERROR: Invalid hash algorithm.'); 
if($count <= 0 || $key_length <= 0) 
    die('PBKDF2 ERROR: Invalid parameters.'); 

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

$output = ""; 
for($i = 1; $i <= $block_count; $i++) { 
    // $i encoded as 4 bytes, big endian. 
    $last = $salt . pack("N", $i); 
    // first iteration 
    $last = $xorsum = hash_hmac($algorithm, $last, $password, true); 
    // perform the other $count - 1 iterations 
    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 bin2hex(substr($output, 0, $key_length)); 
} 
?> 
+0

鹽與其他參數一起被包含在散列中。驗證函數提取鹽並使用相同的參數重新設置密碼,這應該產生相同的哈希值。 – NullUserException

+0

好的@NullUserException謝謝。我現在看到我只是儲存出來的整個大字符串,我不需要分解並單獨存儲它。謝謝。 –

回答

0

我發現這個尋找一個老的PHP版本的PBKDF2實現。對於別人發生在打這個頁面,如果你直接使用這種方法:

pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false) 

然後,它不包括與輸出字符串,剛散,這是我希望的鹽。

create_hash方法是原來的海報是用什麼,並與上述他的設置,這將返回:

sha256:1000:$salt:$hash 

這可以讓你給它的所有保存在一個數據庫列,這是很容易使用。但是,我會注意到,如果您關心存儲空間,那麼您「可能」仍然希望將其分開。使用十六進制鹽和密碼,您可以通過將其存儲在二進制列中來減少佔用的一半存儲空間......例如,在MySQL中,這是通過SET binaryColumn=UNHEX('0F0F'),然後SELECT HEX(binaryColumn) AS binaryColumn完成的。這是更多的工作,所以它歸結於你自己的偏好和目標,但認爲我會把它扔到那裏。