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