安全存儲口令的業(yè)界標(biāo)準(zhǔn):bcrypt算法
談一談安全保護(hù)口令的的標(biāo)準(zhǔn)算法,這就是 bcrypt 算法。為了把事情說清楚,分兩篇文章描述:
- 說說 bcrypt 算法,以及通過PHP的crypt()算法進(jìn)一步參數(shù)闡述,雖然crypt()算法已經(jīng)不推薦使用了,但對于理解 bcrypt 算法還是非常好的,可以看看它的歷史。
- 在一個系統(tǒng)中,如果有多種開發(fā)語言,那么 bcrypt 算法是否通用呢?通過 PHP 和 Ptyhon 語言進(jìn)行描述。
本文主要理解 bcrypt 算法,bcrypt 算法可以認(rèn)為是 KDF 函數(shù)的一種實現(xiàn),也有迭代因子的概念。
bcrypt 算法基于 Blowfish 塊密鑰算法,bcrypt 算法已經(jīng)有10多年的歷史,而 Blowfish 密鑰算法更是有20多年的歷史,久經(jīng)考驗,所以被認(rèn)為是 Hash 加密口令的標(biāo)準(zhǔn)算法。
bcrypt 算法在內(nèi)部會使用內(nèi)存初始化 hash 過程,由于需要內(nèi)存,雖然在 CPU 上運行很快,但在 GPU 并行運算卻不快,這也減緩了攻擊者的破解速度。
接下去我使用 PHP 語言中的 crypt() 函數(shù)介紹如何使用 bcrypt 算法,如果你對 Hash 保護(hù)口令了解的不多,那么使用 crypt() 函數(shù)可能會存在很多問題。
首先必須明確 crypt() 函數(shù)并不是 bcrypt 算法,它可以基于多種不同的 Hash 算法。
該函數(shù)的原型:
- string crypt ( string $str [, string $salt ] )
看上去很簡單,但隱藏了很多內(nèi)容。
如果你僅僅調(diào)用 crypt(),會根據(jù)操作系統(tǒng)版本和 PHP 版本使用相應(yīng)的 Hash 算法,而且如果不顯示的輸入 salt,可能會得到一個弱 salt,所以不推薦這樣調(diào)用 crypt() 函數(shù),因為屏蔽了很多細(xì)節(jié)。
那么如何選定 bcrypt 算法(Blowfish)、迭代因子、salt,先看一個例子:
- if (CRYPT_BLOWFISH == 1) {
- echo 'Blowfish:' . crypt('abcde', '$2a$07$woshiyigesaltzhi') . "\n";
- }
- 表示使用 bcrypt 算法(注意:如果是 PHP 5.3.7 后續(xù)版本,使用 ,修復(fù)了安全風(fēng)險)。
- 07 表示迭代次數(shù)為 7 次。
- 最后一個 $ 后面內(nèi)容為 salt 值。
接下去看看上面代碼的輸出:
- Blowfish:$2a$07$woshiyigesaltzhi$$$$$.lrU488y7E1Xw.JA4uizIu.PBSSe7t4y
也就是說返回值包含了 crypt() 函數(shù)相關(guān)信息,比如告訴你使用了 bcrypt 算法,迭代因子是 7,salt 是 woshiyigesaltzhi$$$$$,剩下的部分就是口令密文。
此處,遺留一個問題,crypt() 運算出來的口令密文包含 salt 是否不安全?這會在下一篇中描述。
crypt() 也可以使用其它的 Hash 函數(shù),比如:
- CRYPT_MD5:
- CRYPT_BLOWFISH: 或
- CRYPT_SHA256:
- CRYPT_SHA512:
大家大概明白 crpyt() 函數(shù)的使用了,可能使用的時候有點麻煩,所以建議包裝下該函數(shù):
- function better_crypt($input, $rounds = 7) {
- $salt = openssl_random_pseudo_bytes(22);
- return crypt($input, sprintf('$2a$%02d$', $rounds) . $salt);
- }
不管怎么說,crypt() 函數(shù)完全基于底層的 C 函數(shù),運行環(huán)境也依賴于操作系統(tǒng)和PHP版本,系統(tǒng)和代碼遷移的時候可能有多種問題,所以 PHP 從 5.5 版本以后建議使用Password Hashing Functions,這在下一篇會詳細(xì)說一下。
最后簡單提下 scrypt 算法,它是一種新的口令保護(hù)算法,是另外一種思路,被認(rèn)為是口令保護(hù)的業(yè)界標(biāo)準(zhǔn)算法,但由于時間較短,現(xiàn)在建議還是使用 bcrypt() 算法。