##### 最簡單的用戶密碼保存方式
用戶訪問網站,在注冊時需要輸入用戶名和密碼,在僅考慮功能的前提下,可以把用戶名和密碼以明文的方式存儲在數據表中。
| Username | Password |
| --- | --- |
| foobar | foobar#123456 |
當用戶登錄時,直接使用用戶提供的明文密碼與數據庫中的密碼進行比對,如果相同則授權用戶登錄。
##### 面臨的安全問題 : 數據庫被竊取導致密碼泄露
明文密碼保存方式在實現上非常簡單,但面臨的問題也很明顯:沒有任何安全防護機制,任何有權讀取數據庫的人都可以獲取所有用戶的密碼。因此我們需要一種即使數據文件被竊取,竊取者也不能獲取用戶密碼的方式。
##### 解決方式:使用單向hash方式散列用戶密碼
通過hash的方式,可以比較有效的降低密碼泄露的風險。 hash是把輸入的數據流通過特定的算法處理后,輸出是一個固定長度的字符串。如”foobar#123456”經過md5之后,值為”1276563ea8d2c0ef9b04979d59f5d93e”, 把這個字符串保存到數據庫中替代密碼明文。
| Username | Password |
| --- | --- |
| foobar | 1276563ea8d2c0ef9b04979d59f5d93e |
當用戶登錄時,把用戶輸入的密碼經過md5之后與數據庫中的字符串進行對比。
通過hash的處理,即使數據庫文件被竊取,竊取者也不能直觀的獲取到foobar賬號的密碼是什么。
hash的特點是單向的:由輸入流可以得到確定的輸出字符串,但從輸出的字符串反向獲取輸入流的代價極高,幾乎是不可能做到的。
常見的hash處理方式是md5, sha1, sha256, sha512 等。
* md5的輸出是16byte,128bit 的字符串;
* sha1的輸出是20byte,160bit的字符串;
* sha256的輸出是32byte,256bit的字符串;
* sha512的輸出是64byte,512bit的字符串;輸出結果的長度越大,hash方式的安全性就越高, 目前常用的是md5和sha1.
##### 簡單hash方式面臨的問題: Rainbow攻擊
使用md5,sha1等hash防止直接對用戶密碼進行加密,是存在一定的隱患的。 竊取者可以通過Rainbow的方式進行暴力破解。
Rainbow的方式很簡單, 首先根據密碼所使用字符范圍和密碼長度構造出所有可能的密碼序列(稱為詞典),然后對這些密碼序列進行hash(如md5), 然后把hash后的字符串與數據庫中的Password字段進行對比。 這樣可以非常快速的比對到foobar賬號的真實密碼是什么。有測試表明,破解一個7位長度,有a-z,A-Z,0-9構造出來的密碼,只需要不到40秒的時間就可以了, 非常快。
##### 改進的Hash處理方式:對抗Rainbow攻擊
1. 讓用戶使用更長,含有更多特殊字符的密碼
如讓用戶必須使用含有數字、大小寫字母,甚至包括[!@#$%](mailto:!@#%24%)^&\*( 這種特殊字符的密碼。這種方式能提高Rainbow攻擊的hash詞典的構造時間和比對時間,但由于以下兩個原因,不建議過度使用該方式。
a. 由于現在CPU的計算能力極為強大,詞典的構造時間并不是關鍵因素
b. 復雜的密碼用戶很難記憶,用戶體驗太差
3. 使用更高長位的hash方式
相比于md5和sha1,可以使用sha256或者sha512的方式,更長的hash串意味著攻擊者需要花費更多的時間準備詞典。但基于CPU計算能力的強大,這種方式的安全性仍然不足。
5. 使用salt進行hash
謂salt,其實是一個普通的字符串。在對密碼進行hash之前,先把這個字符串與密碼連接起來。如作為明文密碼的前綴或者后綴,然后對新的字符串進行hash操作。 salt通常保存在代碼中,這樣即使數據庫文件丟失,只要代碼源文件不丟失,攻擊者是沒有辦法獲取原始密碼的。
7. 使用速度較慢的hash方式
md5,shax系列的方式,都是通過優化的算法,每次的計算都極為迅速,攻擊者準備攻擊詞典的時間也就大大的縮短。因此我們可以選擇較慢的hash方式,如bcrypt。
bcrypt是一種基于blowfish算命的hash方式,其特點是設計者可以根據選擇hash的時間消耗。 如設置高等級的時間消耗,一次bcrypt甚至可以需要300毫秒才能計算完畢,而通常的md5甚至不需要1毫秒。 這樣可以讓攻擊者的詞典準備時間延長300倍,極大的增加了攻擊時間成本。
##### 完美的解決方案
1. 使用bcrypt慢hash方法
2. 為每個用戶的hash過程提供salt
3. 使用全局salt
通過這三個步驟, 密碼的生成過程可這樣表示 :
$hashPasswd = hash( $global\_salt + hash(cleartext\_password + $user\_only\_salt));
##### 推薦的解決方案
基于時間成本、項目復雜度和安全讀需求,常規Web類項目的推薦解決方案如下
1. 使用md5或者sha1
2. 為每個用戶的hash過程提供salt
3. 使用全局salt
密碼的生成過程為:
$hashPasswd = sha1( $global\_salt + sha1(cleartext\_password + $user\_only\_salt));