密碼雜湊安全
本部分解釋使用雜湊函式對密碼進行安全處理背後的原因, 以及如何有效的進行密碼雜湊處理。
- 為什麼需要把應用程式中使用者的密碼進行雜湊化?
-
當設計一個需要接受使用者密碼的應用時, 對密碼進行雜湊是最基本的,也是必需的安全考慮。 如果不對密碼進行雜湊處理,那麼一旦應用的數據庫受到攻擊, 那麼使用者的密碼將被竊取。 同時,竊取者也可以使用使用者賬號和密碼去嘗試其他的應用, 如果使用者沒有為每個應用單獨設定密碼,那麼將面臨風險。
通過對密碼進行雜湊處理,然後再儲存到數據庫中, 這樣就使得攻擊者無法直接獲取原始密碼, 同時還可以保證你的應用可以對原始密碼進行相同的雜湊處理, 然後比對雜湊結果。
需要著重提醒的是,密碼雜湊只能保護密碼 不會被從數據庫中直接竊取, 但是無法保證注入到應用中的 惡意代碼攔截到原始密碼。
- 為何諸如 md5() 和 sha1() 這樣的常見雜湊函式不適合用在密碼保護場景?
-
MD5,SHA1 以及 SHA256 這樣的雜湊演算法是面向快速、高效 進行雜湊處理而設計的。隨著技術進步和計算機硬體的提升, 破解者可以使用「暴力」方式來尋找雜湊碼 所對應的原始數據。
因為現代化計算機可以快速的「反轉」上述雜湊演算法的雜湊值, 所以很多安全專家都強烈建議 不要在密碼雜湊中使用這些雜湊演算法。
- 如果不建議使用常用雜湊函式保護密碼, 那麼我應該如何對密碼進行雜湊處理?
-
當進行密碼雜湊處理的時候,有兩個必須考慮的因素: 計算量以及「鹽」。 雜湊演算法的計算量越大, 暴力破解所需的時間就越長。
PHP 5.5 提供了 一個原生密碼雜湊 API, 它提供一種安全的方式來完成密碼 雜湊和 驗證。 PHP 5.3.7 及後續版本中都提供了一個 » 純 PHP 的相容庫。
PHP 5.3 及後續版本中,還可以使用 crypt() 函式, 它支援多種雜湊演算法。 針對每種受支援的雜湊演算法,PHP 都提供了對應的原生實現, 所以在使用此函式的時候, 你需要保證所選的雜湊演算法是你的系統所能夠支援的。
當對密碼進行雜湊處理的時候,建議採用 Blowfish 演算法, 這是密碼雜湊 API 的預設演算法。 相比 MD5 或者 SHA1,這個演算法提供了更高的計算量, 同時還有具有良好的伸縮性。
如果使用 crypt() 函式來進行密碼驗證, 那麼你需要選擇一種耗時恒定的字串比較演算法來避免時序攻擊。 (譯註:就是說,字串比較所消耗的時間恒定, 不隨輸入數據的多少變化而變化) PHP 中的 == 和 === 操作符 和 strcmp() 函式都不是耗時恒定的字串比較, 但是 password_verify() 可以幫你完成這項工作。 我們鼓勵你儘可能的使用 原生密碼雜湊 API。
- 「鹽」是什麼?
-
加解密領域中的「鹽」是指在進行雜湊處理的過程中 加入的一些數據,用來避免從已計算的雜湊值表 (被稱作「彩虹表」)中 對比輸出數據從而獲取明文密碼的風險。
簡單而言,「鹽」就是爲了提高雜湊值被破解的難度 而加入的少量數據。 現在有很多線上服務都能夠提供 計算后的雜湊值以及其對應的原始輸入的清單, 並且數據量極其龐大。 通過加「鹽」就可以避免直接從清單中查詢到對應明文的風險。
如果不提供「鹽」,password_hash() 函式會隨機產生「鹽」。 非常簡單,行之有效。
- 我應該如何儲存「鹽」?
-
當使用 password_hash() 或者 crypt() 函式時, 「鹽」會被作為產生的雜湊值的一部分返回。 你可以直接把完整的返回值儲存到數據庫中, 因為這個返回值中已經包含了足夠的資訊, 可以直接用在 password_verify() 或 crypt() 函式來進行密碼驗證。
下圖展示了 crypt() 或 password_hash() 函式返回值的結構。 如你所見,演算法的資訊以及「鹽」都已經包含在返回值中, 在後續的密碼驗證中將會用到這些資訊。