你們都知道散列算法,如 MD5, SHA, 可是對其具體特性恐怕都很模糊。說不許哪些用法是可靠的,哪些用法是不可靠的,只是經過加 salt, 或者反覆散列的方式提升可靠性。本文將精確地討論它們在各類使用方法下的可靠性,本文不討論原理,只討論使用,如下部分資料來自網絡,感謝 wxy 幫忙檢驗文中的論斷。算法
MD5 和 SHA 系列算法都屬於同一類——我還沒給這類算法找到一個足夠貼切的名字。首先在大的分類上,它們都是散列算法。
散列是怎麼個定義呢?典型的散列算法能夠是任何一個:具備無限的定義域,且具備有限的值域的函數。甚至,寬鬆的廣義散列算法能夠是任何一個(數學意義上的)函數,由於函數自己的概念就是將一個或多個值映射到一個惟一的值。數據庫
從具體特徵上來講,它們(的目標)都是足夠安全的信息摘要算法。究竟何謂安全?
MD5 和 SHA 系列函數所追求的安全,在於下面兩個方面。緩存
所謂單向性,就是指明文 M, 通過散列後的 hash(M). 從 hash(M) 沒法推算出 M. 這是大部分摘要算法都具備的特徵,由於大多數狀況下,被摘要的信息長度要小於散列值,由於缺乏信息,且值域明顯小於定義域,即一個散列值會對應多個(無限個)原文,因此自然地就沒法從散列值反向計算出原文。MD5 和 SHA 系列函數在單向性方面目前都尚未被發現明顯的漏洞。安全
可是以上討論的只是理論上的單向性,實際使用中,攻擊者能夠經過窮舉,即暴力破解的方式推算出可能的原文。這種狀況適用於已知信息原文定義域且範圍較小的狀況。例如若是直接對用戶的密碼進行散列,由於密碼大多在十位字母左右,攻擊者能夠以很低的成本計算全部密碼組合的散列值,以獲得可能的信息原文。網絡
從散列算法的角度,應對暴力破解攻擊沒有明顯有效的手段,但也能夠經過『提升計算成本』和『減小散列值長度』的方式提高單向性。分佈式
提升計算成本即經過將算法設計得更爲複雜,增長更屢次計算,來提升對單個信息進行散列的時間。對於正常單次散列來說,即便增長十倍的計算時間,仍然是能夠接受的。但當攻擊者進行暴力破解時,一樣須要原來十倍的計算時間,這個成本對攻擊者來講可能沒法接受。除此以外,還能夠改造算法,使其只能運行於 CPU 上,沒法使用顯卡或專用芯片進行計算,以提升暴力破解的計算成本。
但彷佛這種方式並無被業界接受,SHA 系列函數中的新成員均減小而不是提升了計算成本。函數
減小散列值長度,即經過減小散列值的長度來使得多個(大量)信息原文映射到同一個散列值,使攻擊者沒法分辨究竟哪一個纔是真正的原文。
例如將散列值減小到 8 bit, 這樣密碼組合中 1/256 的密碼都會映射到同一個散列值,根本沒法分別哪一個纔是真正的密碼。
這種方式顯然很扯淡,由於這種方式會明顯下降下面要提到的抗衝突性。優化
實際使用中,咱們能夠經過提升定義域範圍的方式,來加強單向性。一樣是散列用戶密碼的例子,咱們能夠將原來的 hash(M) 改成 hash(hash(M)). 由於 hash 的值域範圍一般要比密碼(約 10 位字母,即約 60 bit)大, 如 MD5 的值域是 128 bit, SHA-1 的值域是 160 bit. 所以咱們經過第一次散列將第二次散列的定義域提升到了 2-4 倍,必定程度上提升了攻擊者暴力破解的成本。網站
可是,攻擊者會將 hash(hash(x)) 視爲一個獨立的散列函數 hash2(x), 除了正常地增長了一倍的計算時間,並無受到咱們增長做用域的影響。所以,這時咱們須要引入 salt(佐料,干擾), 將散列算法改成 hash(hash(x + salt)), salt 須要與散列值一同儲存。設計
在已知 salt 的狀況下,對攻擊者其實沒有任何影響,由於這個函數依舊能夠轉換爲 hash2(x + salt), 固然,有些攻擊者會經過 Rainbow Table(彩虹表) 的方式進行協做和緩存,加 salt 會使他們沒法使用 Rainbow Table.
Rainbow Table, 即事先對預約範圍的數據進行散列,儲存散列結果。而後經過數據庫或者分佈式技術(小型 Rainbow Table 就不須要了), 來優化從散列值到原文的『反向』查詢。類似的技術還有『字典』,即收集你們經常使用的原文(密碼,或其餘短語), 代替窮舉,以提升暴力破解的效率。
而在這個場景中,將 hash(hash(x + salt)) 更換爲 hash(hash(x) + salt) 會得到更好的效果。由於前一種狀況,攻擊者能夠將密碼和 salt 視爲一個總體來使用 Rainbow Table (雖然在 Rainbow Table 中極可能查不到這些信息); 後者則不能。由於後者是一個散列值加一個 salt, 長度已經遠遠超出了 Rainbow Table 的範圍。固然,單純加長 salt 也能夠實現 hash(hash(x) + salt) 的效果。
在攻擊者不知道 salt 的狀況下,有兩種選擇。
一是將 hash2(x) 改成 hash2(x, salt), 這種狀況適用攻擊者知道 salt 的長度或範圍的狀況,由於這個函數有兩個參數,所以破解成本與 x 或 salt 的長度呈指數函數,只要 salt 足夠長,那麼能夠認爲攻擊者即便暴力破解也無能爲力。
二是將第二個 hash 的輸入視爲一個散列值,換言之就是進行兩次爆破,這種方式的計算成本相比於前一種會更高,由於暴力破解的成本同原文長度也是呈指數關係的。
因此,在未知 salt 的狀況下,能夠防護全部暴力破解,除非攻擊者擁有極其驚人的計算力。
舉個例子,若是密碼爲 10 位的字母,salt 爲 3 位的字母,暴力破解須要至關於 Bitcoin 全網計算力的約 60 天時間。
雖然咱們還擁有更強大的變種,例如:
hash(x, salt[]) = hash(hash(x + salt[1]) + hash(x + salt[2]) + ...)
可是咱們認爲,使用 hash(hash(x) + salt) 和足夠長的 salt(大體至關於散列值的長度), 已經足夠安全。
接下來,那麼如何生成 salt 呢?是否有必要爲每條數據(仍是前面的例子,每一個用戶)使用單獨的 salt 呢。經過前面的討論咱們看到,要保證單向性,對 salt 進行保密十分必要。若是你可以保證 salt 的絕對安全的話,那麼是否使用單獨的 salt 其實可有可無。不要擔憂使用相同的 salt 會讓攻擊者找到某種規律而計算出 salt(固然前提是 salt 足夠長), 由於 MD5, SHA 系列函數保證了單向性,攻擊者沒法從散列值推測出原文的任何信息。
至於 salt 的取值,沒有什麼特別的要求,基於保密的考慮,一般要使用隨機生成的字符串,而後就是足夠長(大約等於散列值的長度).
但咱們認爲仍有必要爲每條數據使用單獨的 salt, 這是基於下面兩點考慮。
一是雖然暴力破解須要驚人的計算力,可是若是隻需破解一個散列值,就能夠獲知全部數據的 salt, 這個破解也是值得的,也許攻擊者就可以提供這麼多的計算力也說不定,雖然若是你使用足夠長的 salt 的話,這種可能性極小。
二是也許你不能保證 salt 的絕對安全,當你的 salt 泄漏後,每條數據使用不一樣的 salt 將會爲攻擊者製造最後一道障礙。
若是你全部的數據都使用一樣的 salt, 那麼攻擊者在拿到數據後,只需進行一次暴力破解,便可破解出全部的數據(若是都命中了的話), 但若是你爲每條數據使用不一樣的 salt, 那麼攻擊者將不得不爲每條數據單獨運行一次暴力破解,由於每條數據至關於在使用不一樣的散列函數,由於 salt 是不一樣的。
綜上,咱們認爲網站的用戶系統使用下面的散列便可保證安全性:
hash(hash(passwd) + salt)
其中 salt 是隨機生成的,長度等於散列值長度的字符串。hash 能夠是 MD5 或 SHA 系列函數。
咱們接着來談抗衝突性,所謂抗衝突性即從計算上不可能找到兩個有一樣散列值的原文。抗衝突性分爲抗強衝突性,和抗弱衝突性。
抗強衝突性,即給定一個散列值,沒法找到另外一個具備一樣散列值的信息原文。
抗弱衝突性,即沒法找到兩個具備一樣散列值的信息原文。
加上單向性,這三條特性是呈階梯狀的,知足『抗弱衝突性』就必定知足『抗強衝突性』也就必定知足『單向性』同時也就知足『散列映射具備隨機性和均勻性』。MD5 和 SHA 系列函數在設計上都知足這三條特徵,但目前已經被發現了存在某些漏洞。
抗衝突性的主要應用場景就是爲信息原文進行摘要,鑑別兩段信息原文是否相同。
而攻擊者的目標就是,對信息原文進行修改,而後在信息原文後(或者修改信息原文)添加給定的數據段,使其可以生成與以前相同的散列值,實現僞造數據原文。
在這種場景下攻擊者是否也可以進行暴力破解呢?很難,由於在這種狀況下,攻擊者須要不斷更換附加在信息原文後的數據,以得到和以前相同的散列值,在不理想的狀況下,須要嘗試全部的散列值,這個計算力被認爲是沒法接受的。但經過算法的一些漏洞,能夠下降這個暴力破解的成本。
抗衝突性是檢驗散列算法設計是否優秀的重要指標,不少散列算法被淘汰都是由於抗衝突性存在漏洞。
目前 MD5 已在 2005 年被中國數學家王小云發現有抗強衝突性的漏洞,給定一個散列值,能夠在幾分鐘內用普通計算機找到一個碰撞,即具備相同散列值的信息原文。
SHA-0 被發現漏洞能夠將尋找碰撞的難度從 2^80 次暴力破解下降到 2^39 次,SHA-1 被發現漏洞能夠將尋找碰撞的難度從 2^80 次下降到 2^63 次,SHA-2 系列函數還未發現漏洞。
所以目前使用 MD5 進行信息摘要並不安全,攻擊者能夠輕易地僞造一段散列值相同,但內容不一樣的原文,雖然攻擊者還不可以自由地修改原文。 而 SHA-1 和 SHA-2 系列函數目前來說是足夠安全的。