密碼散列安全

本部分解釋使用散列函數(shù)對密碼進(jìn)行安全處理背后的原因, 以及如何有效的進(jìn)行密碼散列處理。

為什么需要把應(yīng)用程序中用戶的密碼進(jìn)行散列化?

當(dāng)設(shè)計一個需要接受用戶密碼的應(yīng)用時, 對密碼進(jìn)行散列是最基本的,也是必需的安全考慮。 如果不對密碼進(jìn)行散列處理,那么一旦應(yīng)用的數(shù)據(jù)庫受到攻擊, 那么用戶的密碼將被竊取。 同時,竊取者也可以使用用戶賬號和密碼去嘗試其他的應(yīng)用, 如果用戶沒有為每個應(yīng)用單獨設(shè)置密碼,那么將面臨風(fēng)險。

通過對密碼進(jìn)行散列處理,然后再保存到數(shù)據(jù)庫中, 這樣就使得攻擊者無法直接獲取原始密碼, 同時還可以保證你的應(yīng)用可以對原始密碼進(jìn)行相同的散列處理, 然后比對散列結(jié)果。

需要著重提醒的是,密碼散列只能保護(hù)密碼 不會被從數(shù)據(jù)庫中直接竊取, 但是無法保證注入到應(yīng)用中的 惡意代碼攔截到原始密碼。

為何諸如 md5()sha1() 這樣的常見散列函數(shù)不適合用在密碼保護(hù)場景?

MD5,SHA1 以及 SHA256 這樣的散列算法是面向快速、高效 進(jìn)行散列處理而設(shè)計的。隨著技術(shù)進(jìn)步和計算機(jī)硬件的提升, 破解者可以使用“暴力”方式來尋找散列碼 所對應(yīng)的原始數(shù)據(jù)。

因為現(xiàn)代化計算機(jī)可以快速的“反轉(zhuǎn)”上述散列算法的散列值, 所以很多安全專家都強烈建議 不要在密碼散列中使用這些散列算法。

如果不建議使用常用散列函數(shù)保護(hù)密碼, 那么我應(yīng)該如何對密碼進(jìn)行散列處理?

當(dāng)進(jìn)行密碼散列處理的時候,有兩個必須考慮的因素: 計算量以及“鹽”。 散列算法的計算量越大, 暴力破解所需的時間就越長。

PHP 5.5 提供了 一個原生密碼散列 API, 它提供一種安全的方式來完成密碼 散列驗證。 PHP 5.3.7 及后續(xù)版本中都提供了一個 ? 純 PHP 的兼容庫

PHP 5.3 及后續(xù)版本中,還可以使用 crypt() 函數(shù), 它支持多種散列算法。 針對每種受支持的散列算法,PHP 都提供了對應(yīng)的原生實現(xiàn), 所以在使用此函數(shù)的時候, 你需要保證所選的散列算法是你的系統(tǒng)所能夠支持的。

當(dāng)對密碼進(jìn)行散列處理的時候,建議采用 Blowfish 算法, 這是密碼散列 API 的默認(rèn)算法。 相比 MD5 或者 SHA1,這個算法提供了更高的計算量, 同時還有具有良好的伸縮性。

如果使用 crypt() 函數(shù)來進(jìn)行密碼驗證, 那么你需要選擇一種耗時恒定的字符串比較算法來避免時序攻擊。 (譯注:就是說,字符串比較所消耗的時間恒定, 不隨輸入數(shù)據(jù)的多少變化而變化) PHP 中的 == 和 === 操作符strcmp() 函數(shù)都不是耗時恒定的字符串比較, 但是 password_verify() 可以幫你完成這項工作。 我們鼓勵你盡可能的使用 原生密碼散列 API。

“鹽”是什么?

加解密領(lǐng)域中的“鹽”是指在進(jìn)行散列處理的過程中 加入的一些數(shù)據(jù),用來避免從已計算的散列值表 (被稱作“彩虹表”)中 對比輸出數(shù)據(jù)從而獲取明文密碼的風(fēng)險。

簡單而言,“鹽”就是為了提高散列值被破解的難度 而加入的少量數(shù)據(jù)。 現(xiàn)在有很多在線服務(wù)都能夠提供 計算后的散列值以及其對應(yīng)的原始輸入的清單, 并且數(shù)據(jù)量極其龐大。 通過加“鹽”就可以避免直接從清單中查找到對應(yīng)明文的風(fēng)險。

如果不提供“鹽”,password_hash() 函數(shù)會隨機(jī)生成“鹽”。 非常簡單,行之有效。

我應(yīng)該如何保存“鹽”?

當(dāng)使用 password_hash() 或者 crypt() 函數(shù)時, “鹽”會被作為生成的散列值的一部分返回。 你可以直接把完整的返回值存儲到數(shù)據(jù)庫中, 因為這個返回值中已經(jīng)包含了足夠的信息, 可以直接用在 password_verify()crypt() 函數(shù)來進(jìn)行密碼驗證。

下圖展示了 crypt()password_hash() 函數(shù)返回值的結(jié)構(gòu)。 如你所見,算法的信息以及“鹽”都已經(jīng)包含在返回值中, 在后續(xù)的密碼驗證中將會用到這些信息。


        password_hash 和 crypt 函數(shù)返回值的組成部分,依次為:所選擇的算法,
        算法選項,所使用的“鹽”,
        以及散列后的密碼。