如何正確對用戶密碼進行加密


摘要

密碼驗證是很常見的需求,如何在實現功能之余,防止用戶密碼泄露,已經有了很成熟的方案。這篇文章把自己的思考和結論做一下記錄。

結論

對用戶密碼進行加密時需要做到:

  • 防止用戶密碼明文被竊聽
    1.交給https,明文傳輸。
    2.客戶端將密碼加鹽(鹽隨機生成、具有強度)並哈希。服務端再次加鹽哈希並對比。假設https被竊聽,攻擊者破解密碼明文也具有相當難度。
  • 防止數據庫被攻破時,用戶密碼明文被竊取。
    1.增加哈希算法強度。
    2.隨機生成具有強度的鹽。

一些思考

哈希算法是不可逆的。攻擊者可以生成海量的密碼 -> 哈希值鍵值對,反向映射,有概率通過哈希值得到密碼。
故,破解的成本=哈希算法強度×鹽值數量。

如何選擇哈希算法強度

  • 計算耗時用戶可接受(視應用場景,如0.2S內)。
  • 計算耗時盡量長,即增加哈希算法強度。

為什么鹽要隨機

如果鹽不隨機,攻擊者可以針對單個鹽生成哈希值->密碼鍵值對,再對整個數據庫的哈希值做匹配。
假設鹽是保密的,鹽可能因為代碼泄漏、社會工程學被攻擊者獲取。
攻擊者也可以通過在數據庫被攻破的網站上注冊用戶,通過 哈希值->攻擊者密碼+鹽 來破解鹽。

為什么鹽要有強度

如果鹽的強度(長度)不夠。攻擊者可以建立多個 哈希值->密碼 數據庫,簡單鹽被匹配(攻破)的概率更高。

為什么鹽可以明文存儲

攻擊者很難有足夠的計算資源和存儲空間建立海量的 哈希值->密碼 數據庫,針對單條用戶記錄,建立 哈希值->密碼 數據庫進行攻擊的成本過高。

php的實現

最初的想法

需求是房間密碼,出於簡單考慮,我最初的想法是,MD5+隨機鹽。
在數據庫里大致是這樣:

+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| uid  | int(11)    | NO  | PRI | NULL    |      |
| pwd  | varchar(45) | YES  |    | NULL    |      |
| salt  | varchar(45) | YES  |    | NULL    |      |
+-------+-------------+------+-----+---------+-------+

php的推薦實現

php的md5文檔
http://php.net/manual/zh/function.md5.php
給了一個很好的指引:
http://php.net/manual/zh/faq.passwords.php#faq.passwords.fasthash

password_hash 和 crypt 函數返回值的組成部分,依次為:所選擇的算法,算法選項,所使用的“鹽”,以及散列后的密碼。
更改后、數據庫表變為:

+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| uid  | int(11)    | NO  | PRI | NULL    |      |
| pwd  | varchar(255) | YES  |    | NULL    |      |
+-------+-------------+------+-----+---------+-------+

相較之前的方案,獲得了
1.記錄了采用的算法(可以在不改動代碼的情況下升級算法)
2.記錄了采用算法的cost(強度),可以在硬件計算能力上升的情況下,調整cost來維持安全性。
3.鹽和哈希值一並返回,簡化了接口調用、數據庫存儲。
代碼:

if (!empty($xxxx_info['pwd'])) {    // 若原來有密碼,則要檢測
    if (!password_verify($old_pwd, $xxxx_info['pwd'])) {
        // 用戶名或密碼錯
        return;
    }
}
// 對密碼長度、內容等不做限制。
// 以應用場景來說,123456之類也無所謂。
$pwd_in_db = password_hash($new_pwd, PASSWORD_DEFAULT, array("cost" => 6));  

參考

http://php.net/manual/zh/faq.passwords.php#faq.passwords.fasthash
http://www.infoq.com/cn/articles/how-to-encrypt-the-user-password-correctly


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM