2010-09-29 49 views
17

我一直在使用PHP的crypt()作爲存儲和驗證數據庫密碼的一種方式。我使用散列來處理其他事情,但使用crypt()作爲密碼。文件不是很好,似乎有很多爭論。我使用河豚和兩種鹽來密碼並將其存儲在數據庫中。在我存儲salt和加密密碼之前(像鹽漬散列),但是實現了冗餘,因爲salt是加密密碼字符串的一部分。我正確使用PHP的crypt()函數嗎?

我對於如何在crypt()上運行彩虹表攻擊感到有點困惑,無論如何,從安全的角度來看,這看起來是正確的。我使用第二個salt來追加密碼以增加短密碼的熵,可能是過度的,但爲什麼不呢?

function crypt_password($password) { 
if ($password) { 
    //find the longest valid salt allowed by server 
    $max_salt = CRYPT_SALT_LENGTH; 

    //blowfish hashing with a salt as follows: "$2a$", a two digit cost parameter, "$", and 22 base 64 
    $blowfish = '$2a$10$'; 

    //get the longest salt, could set to 22 crypt ignores extra data 
    $salt = get_salt ($max_salt); 

    //get a second salt to strengthen password 
    $salt2 = get_salt (30); //set to whatever 


    //append salt2 data to the password, and crypt using salt, results in a 60 char output 
    $crypt_pass = crypt ($password . $salt2, $blowfish . $salt); 

    //insert crypt pass along with salt2 into database. 
    $sql = "insert into database...."; 

    return true; 
    } 
} 


function get_salt($length) { 
$options = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./'; 

$salt = ''; 

for($i = 0; $i <= $length; $i ++) { 
    $options = str_shuffle ($options); 
    $salt .= $options [rand (0, 63)]; 
} 
return $salt; 
} 

function verify_password($input_password) 
{ 
if($input_password) 
{ 
    //get stored crypt pass,and salt2 from the database 
    $stored_password = 'somethingfromdatabase'; 
    $stored_salt2 = 'somethingelsefromdatabase'; 

    //compare the crypt of input+stored_salt2 to the stored crypt password 
    if (crypt($input_password . $stored_salt2, $stored_password) == $stored_password) { 
     //authenticated 
     return true; 
    } 
    else return false; 
} 
else return false; 
} 
+0

使用'mt_rand'而不是'rand'會對你的腳本有一點改進 – Sliq 2012-09-08 21:38:16

回答

15

你真的應該看看PHPASS:http://www.openwall.com/phpass/這是一個使用crypt()的密碼哈希框架,用於像Wordpress和phpBB這樣的項目。

還有本網站有關的密碼哈希上的優秀文章,醃製和使用的crypt()拉伸:http://www.openwall.com/articles/PHP-Users-Passwords

UPDATE: 目前有針對PHPASS庫的替代品。在下一個版本的PHP中,有特殊的函數用於哈希和驗證密碼(使用bcrypt):http://www.php.net/manual/en/ref.password.php。有一個兼容性庫,實現這些函數爲PHP 5.3.7+:https://github.com/ircmaxell/password_compat

+1

我可以看到該框架如何對開箱即用方法有所幫助,但我的代碼和代碼phpass代碼之間沒有太大差別。 – Brian 2010-09-29 23:50:25

+1

並且:PHPass是一團糟。巨大的混亂。這太混亂了,我不會依賴它。 – Sliq 2012-09-08 21:25:42

+0

@Panique你願意解釋一下嗎? – 2012-10-12 15:14:43

3

彩虹表的想法是,攻擊者可以讓一個表,所有可能的密碼,並在回家的哈希值。

E.g.

PASSWORD HASH 
iloveSO gjroewjgo 
password knbnogjwm 
secret gjroehghe 
jbieber rewgroewj 

有了這張表,攻擊者可以任意哈希迅速轉換成密碼。彩虹表使用一些技巧,所以並非所有的哈希都必須存儲,但它仍然會事先計算所有哈希值。

通過使用鹽,即使在使用密碼進行存儲時,您也會更加困難。而不是在字典中散列每個單詞,攻擊者現在必須用每個鹽來散列每個詞。用足夠長的鹽,這給了足夠的組合,使得計算所有這些哈希值變得不可行。

所以鹽並不意味着是一個額外的密碼,只有應用程序才知道,它的目的是改變散列函數,使它不是標準的。

+0

他正在使用鹽,但它的長度不確定。如果有大量的鹽值,這樣的彩虹表將不可行。例如256^256或256字節的base256鹽是如此之大,以至於我們甚至沒有一個單詞來解釋破解只有一個字符的密碼所需的字節數。 – rook 2010-09-29 19:13:29

2

這是濫用crypt(),因爲您使用的是已棄用的原語。河豚年紀很大,兩頭是替代品,甚至是舊的,因爲三頭魚幾乎已經敲定。你應該使用sha2家族的成員,sha256或sha512都是不錯的選擇。 crypt()可以與sha256或sha512一起使用,您應該分別使用CRYPT_SHA256 CRYPT_SHA512參數。

此外,您的鹽的熵/體積比非常小,因爲字母數字彩虹表最常見,所以只使用字母數字組合。你應該使用base256的完整字節,並且我推薦長度爲256字節的salt。請記住,所有哈希函數根據定義是二進制安全的,因此您不必擔心空字節等。

+0

河豚只能使用字母數字字符,所以也許我應該使用CRYPT_SHA512。 – Brian 2010-09-29 22:29:16

+0

@Brian Perin然後這是一個奇怪的crypt(),因爲分組密碼被用來加密文件和流量的所有時間。 – rook 2010-09-29 23:17:51

+2

'crypt()'使用的Blowfish與Blowfish的舊密碼密碼不完全相同 - 它是專門爲密碼散列/密鑰派生而創建的變體算法。 – caf 2010-09-30 07:02:30

11

您使用crypt()是好的。 crypt($input, $stored) == $stored是它被設計使用的方式。

您的get_salt()功能並不好,因爲它使用的是功能經常很差的rand()函數。您應該考慮使用更強的隨機功能,如openssl_random_pseudo_bytes()

0

使用含有time()和openssl_random_pseudo_bytes()的鹽的SHA-512(如果可用)。地穴被合併/有效,因爲它返回插入散列字符串的鹽。