前記
MD5的全稱是Message-Digest Algorithm 5(信息-摘要算法);
特點是不可逆的,一般解密不了;那有沒有想過,為什么各種工具網站都可以進行MD5解密呢?https://www.sojson.com/encrypt_md5.html
彩虹表,了解一下。
1. 如何存儲密碼才是安全的?
密碼存儲有幾種方式:
- 直接存儲密碼明文m
- 存儲密碼明文的哈希值hash(m)
- 存儲密碼明文的加鹽哈希 hash(m+salt),這里的salt可以是用戶名,手機號等,但必須保證每個用戶的salt都不一樣才是安全的。
如果數據庫被入侵。
第一方式,明文存儲,無安全性可言。
第二種方式,雖然是入侵者得到的是hash值,但由於彩虹表的存在,也很容易批量還原出密碼明文來。
只有第三種方式才是相對安全的。
2. 彩虹表不是 密碼-->明文 的簡單存儲
要從c=hash(m)逆向得到原始明文m,有三種辦法:
- 暴力破解法:時間成本太高。
- 字典法:提前構建一個“明文->密文”對應關系的一個大型數據庫,破解時通過密文直接反查明文。但存儲一個這樣的數據庫,空間成本是驚人的。
- 構建彩虹表:在字典法的基礎上改進,以時間換空間。是現在破解哈希常用的辦法。
3. 彩虹表的前身--預先計算的散列鏈
既然存儲所有的明文密碼對需要的空間太大,密碼學家們想出了一種以計算時間降低存儲空間的辦法:“預計算的哈希鏈集”(Precomputed hash chains)。
這是一條k=2哈希鏈:

H函數就是要破解的哈希函數。
約簡函數(reduction function)R函數是構建這條鏈的時候定義的一個函數:它的值域和定義域與H函數相反。通過該函數可以將哈希值約簡為一個與原文相同格式的值。
這條鏈是這樣生成的:
- 隨機選擇一個明文aaaaaa
- 對其求哈希得到281DAF40
- R(281DAF40) 得到另外一個明文sgfnyd。
- 繼續重復2,3步驟
存儲的時候,不需要存儲所有的節點,只需要存儲每條鏈的頭尾節點(這里是aaaaaa和kiebgt)
以大量的隨機明文作為起節點,通過上述步驟計算出哈希鏈並將終節點進行儲存,可得到一張哈希鏈集。
預計算的哈希鏈集的使用
要破解一個hash值,
- 假設其剛好是920ECF10:首先對其進行一次R運算,得到kiebgt,然后發現剛好命中了哈希鏈集中的(aaaaaa,kiebgt)鏈條。可以確定其極大概率在這個鏈條中。於是從aaaaaa開始重復哈希鏈的計算過程,發現sgfnyd的哈希結果剛好是920ECF10,於是破解成功。
- 密文不是“920ECF10”而是“281DAF40”:第一次R運算后的結果並未在末節點中找到,則再重復一次H運算+R運算,這時又得到了末節點中的值“kiebgt”。於是再從頭開始運算,可知aaaaaa剛好可哈希值為281DAF40。
- 如是重復了k(=2)次之后,仍然沒有在末節點中找到對應的值,則破解失敗。
預計算的哈希鏈集的意義
對於一個長度為k的預計算的哈希鏈集,每次破解計算次數不超過k,因此比暴力破解大大節約時間。
每條鏈只保存起節點和末節點,儲存空間只需約1/k,因而大大節約了空間。
R函數的問題
要發揮預計算的哈希鏈集的左右,需要一個分布均勻的R函數。當出現碰撞時,就會出現下面這種情況
111 --H--> EDEDED --R--> 222 --H--> FEDEFE --R--> 333 --H--> FEFEDC --R--> 444
454 --H--> FEDECE --R--> 333 --H--> FEFEDC --R--> 444 -H--> FEGEDC --R--> 555
兩條鏈出現了重疊。這兩條哈希鏈能解密的明文數量就遠小於理論上的明文數2×k。由於集合只保存鏈條的首末節點,因此這樣的重復鏈條並不能被迅速地發現。
4. 彩虹表
彩虹表的出現,針對性的解決了R函數導致的鏈重疊問題:
它在各步的運算中,並不使用統一的R函數,而是分別使用R1…Rk共k個不同的R函數(下划線表示下標)。

這樣一來,及時發生碰撞,通常會是下面的情況:
111 --H--> EDEDED --R1--> 222 --H--> FEDEFE --R2--> 333 --H--> FEFEDC --R3--> 444
454 --H--> FEDECE --R1--> 333 --H--> FEFEDC --R2--> 474 -H--> FERFDC --R3--> 909
即使在極端情況下,兩個鏈條同一序列位置上發生碰撞,導致后續鏈條完全一致,這樣的鏈條也會因為末節點相同而檢測出來,可以丟棄其中一條而不浪費存儲空間。
4.1 彩虹表的使用
彩虹表的使用比哈希鏈集稍微麻煩一些。
- 首先,假設要破解的密文位於某一鏈條的k-1位置處,對其進行Rk運算,看是否能夠在末節點中找到對應的值。如果找到,則可以如前所述,使用起節點驗證其正確性。
- 否則,繼續假設密文位於k-2位置處,這時就需要進行Rk-1、H、Rk兩步運算,然后在末節點中查找結果。
- 如是反復,最不利條件下需要將密文進行完整的R1、H、…Rk運算后,才能得知密文是否存在於彩虹表之中。
4.2 彩虹表中時間、空間的平衡
對於哈希鏈集,最大計算次數為k,平均計算次數為k/2
彩虹表的最大計算次數為1+2+3+……k = k(k-1)/2,平均計算次數為[(k+2) * (k +1)]/6。
可見,要解相同個數的明文,彩虹表的代價會高於哈希鏈集。
無論哈希鏈集還是彩虹表:
當k越大時,破解時間就越長,但彩虹表所占用的空間就越小;
相反,k越小時,彩虹表本身就越大,相應的破解時間就越短。
4.3 常見的彩虹表和R函數舉例
1)常見的彩虹表:http://project-rainbowcrack.com/table.htm
2)R函數舉例:假設明文為5位數字,則R函數是取哈希值中前5個數字。參見https://crypto.stackexchange.com/questions/5900/example-rainbow-table-generation
5. 彩虹表的獲取
可以自己編程生成彩虹表,也可以使用RainbowCrack或Cain等軟件來生成,有興趣的讀者可以自行百度。彩虹表的生成時間與字符集的大小、哈希鏈的長度成正比,如下圖中“7位密碼、全部字符集、哈希鏈長度為2萬”的彩虹表大小為32G,本地生成大約需要332天,而從網上下載只需要2個小時左右,主流的彩虹表的大小普遍在100G以上,想要自己生成是幾乎不可能的事,因此強烈建議黑客技術愛好者直接從網上下載。

彩虹表確實像它的名字一樣美好,至少黑客眼里是這樣。上表是7位以內密碼在不同字符集下構造出的彩虹表的情況,彩虹表中哈希鏈的長度和個數隨着字符集的增長而增長,彩虹表的大小和生成時間也隨之成倍增加。7位數字組合在彩虹表面前簡直就是秒破,即使最復雜的7位密碼不到一個小時就能破解,如果采用普通的暴力攻擊,破解時間可能需要三周。
6. 如何防御彩虹表
雖然彩虹表有着如此驚人的破解效率,但網站的安全人員仍然有辦法防御彩虹表。最有效的方法就是“加鹽”,即在密碼的特定位置插入特定的字符串,這個特定字符串就是“鹽”,加鹽后的密碼經過哈希加密得到的哈希串與加鹽前的哈希串完全不同,黑客用彩虹表得到的密碼根本就不是真正的密碼。即使黑客知道了“鹽”的內容、加鹽的位置,還需要對H函數和R函數進行修改,彩虹表也需要重新生成,因此加鹽能大大增加利用彩虹表攻擊的難度。
7. 為什么加鹽哈希可以抵御彩虹表
彩虹表在生成的過程中,針對的是特定的函數H,H如果發生了改變,則已有的彩虹表數據就完全無法使用。
如果每個用戶都用一個不同的鹽值,那么每個用戶的H函數都不同,則必須要為每個用戶都生成一個不同的彩虹表。大大提高了破解難度。