彩虹表

以前我也跟其他很多人一樣,認爲彩虹表就是描述“明文->密文”對應關係的一個大型數據庫,破解時通過密文直接反查明文。
今天因某些需要而詳細瞭解了彩虹表的一些細節,才發現其原理比之前想象的更值得稱讚。它所謂的time-memory trade-off,並不是簡單地“以空間換時間”,而是雙向的“交易”,在二者之間達到平衡。
在此分享一些心得。
  • 彩虹表的前身
在彩虹表之前,已經出現了對哈希函數的破解算法,被稱爲“預計算的哈希鏈集”(Precomputed hash chains)。

當面對要破解的哈希函數H,首先要定義一個約簡函數(reduction function)R,該函數的定義域和值域需要和哈希函數相反(Q = H(P),P=R(Q)),通過該函數可以將哈希值約簡爲一個與原文相同格式的值("plain text" value)。需要強調的是,由於哈希函數H是不可逆的,所以對於密文進行R運算幾乎不可能得到明文原文。

那麼是如何利用這個R函數構造哈希鏈的呢?對於可能的原文p0,我們做如下運算H(p0)->q1,R(q1)->p1,H(p1)-> q2, R(q2)>p2,H(p2)-> q3,R(q3)->p3  … H(p(n-2))> q(n-1),R(q(n-1))->p(n-1),H(p(n-1))> qn,-R(qn)>pn.好了我們這裏得到一個長度爲N的鏈(一次H運算加一次R運算當做一個節點)。我們只需要把p0和pn保存在表中(待會我們將看到爲什麼只需要這兩個值)。

當我們已知一個哈希值Q我們想找到它對應的H(p?)=Q的P值的時候我們做如下操作:

第一步計算R(Q)=C1,用C1與我們存儲的Pn作比較如果兩者相等,那麼又由:1、H(p(n-1))->qn,R(qn)->pn 2、H(p?)->Q,R(Q)=C1。可知p(n-1)可能跟p?相同。

這時我們在根據p0重新算一遍哈希鏈,算出p(n-1)看它跟p?是不是一樣。

第二步如果C1跟pn不等;那麼我們繼續計算R(Q)->C1,H(C1)->C1~,R(C1~)->C2.得到C2後又與pn比較如果相同那麼又由:1、 H(p(n-2))> q(n-1),R(q(n-1))->p(n-1),H(p(n-1))>qn ,2、H(p?)->Q,R(Q)->C1,H(C1)->C1~,R(C1~)->C2。可知p(n-2)可能就是我們要找的p?,因爲在第一部中我們已經重新計算了哈希鏈,比較p(n-2)和p?看是不是一樣。如此往復找下去直到沒找到,然後查彩虹表裏面的下一條記錄


如果讓我來解釋哈希鏈的意義,我認爲,每一條哈希鏈實際上是代表了屬性相同的一組明文:每一個明文都可以通過起節點迅速的計算得出,計算次數不大於k,因而可以大大節約時間。對每一組明文,只需要保存其特徵值(起節點和末節點),儲存空間只需約1/k,因而大大節約了空間。

  • R的問題
在構造哈希鏈的時候,一個優秀的函數R功不可沒。首先R需要能將值域限定在固定的範圍——例如給定的長度範圍、給定的字符取值範圍等等——之內,否則的話,哈希鏈中大量的計算結果並不在可接受的取值範圍內,一條鏈條無法對應多個明文,鏈條就失去了意義;其次R必須同哈希函數一樣,儘量保證輸出值在值域中的均勻分佈,減少碰撞的概率。
然而實際上,很難找到能滿足這些需求的完美的R函數。,因此這兩條哈希鏈能解密的明文數量就遠小於理論上的明文數n。不幸的是,由於集合只保存鏈條的首末節點,因此這樣的重複鏈條並不能被迅速地發現。隨着碰撞的增加,這樣的重複鏈條會逐漸造成嚴重的冗餘和浪費。

  • 彩虹表的改進點
對於這個問題,2003年提出的彩虹表算法進行了針對性的改進。它在各步的運算中,並不使用統一的R函數,而是分別使用R1…Rk共k個不同的R函數。這樣生成的哈希鏈集即被稱爲彩虹表。(在不同的運算位置使用不同的R函數,就像彩虹由內而外的不同位置上顯示出不同的顏色一樣。)這樣一來,如果發生碰撞,當兩個鏈條發生碰撞的位置並非相同的序列位置時,後續的R函數的不一致使得鏈條的後續部分也不相同,從而最大程度地減小了鏈條中的重複節點,保證了鏈條的有效性。
彩虹表的使用比哈希鏈集稍微麻煩一些。的首先,假設要破解密文位於任一鏈條的k-1位置處,對其進行Rk運算,看是否能夠在末節點中找到對應的值。如果找到,則可以如前所述,使用起節點驗證其正確性。否則,需要繼續假設密文位於k-2位置處,這時就需要進行Rk-1、H、Rk兩步運算,然後在末節點中查找結果。如是反覆,最不利條件下需要將密文進行完整的R1、H、…Rk運算後,才能得知密文是否存在於彩虹表之中。

  • 時間、空間的平衡
通過彩虹表的使用方法可以明顯地看出,一條哈希鏈實際代表的是一組明文的解密規則:
同時,對於相同個數的明文,當n越大時,破解的期望時間就越長,但彩虹表所佔用的空間就越小;相反,n越小時,彩虹表本身就越大,相應的破解時間就越短。這正是保持空間、時間二者平衡的精髓所在。RainbowCrack中rtgen工具使用的默認k值好像是2100。極端的,令n=1,簡化函數R(x)=x,這樣的彩虹表就變成了通常的錯誤理解,即將明文、密文對應關係全部保存的表。此時由於k極小,因而得到的表的體積極大,甚至可能超出儲存能力。(當然,對於範圍較小的明文,如6位以下數字英文的組合,或是全世界常用密碼的集合,生成n=1的表還是不費什麼事的。)

  • 彩虹表的防禦
關於彩虹表的防禦方式,大多與彩虹表的原理,即其生成步驟中用到的函數H有關。
最常用的方法,在其它的答案中也提到了,那就是加鹽(salt),這其實是改變了哈希函數H的形式。由於彩虹表在生成和破解的過程中,都反覆用到了函數H,H如果發生了改變,則已有的彩虹表數據就完全無法使用,必須針對特定的H重新生成,這樣就提高了破解的難度。
防禦彩虹表的另一種方法是提高H函數的計算難度,例如將H定義爲計算一千次MD5後的結果。由於H在算法中的重複性,當單次H函數的計算耗時增加,意味着彩虹表的生成時間會大大的增加,從而也能提高破解的成本。

===========簡化比喻的分隔線==============
如果將哈希後的密文比作一把鎖,暴力破解的方法就是現場製作各種各樣不同齒形的鑰匙,再來嘗試能否開鎖,這樣耗時無疑很長;我以前錯誤理解的“彩虹表”,是事先製作好所有齒形的鑰匙,全部拿過來嘗試開鎖,這樣雖然省去了製作鑰匙的時間,但是後來發現這些鑰匙實在是太多了,沒法全部帶在身上。而真正的彩虹表,是將鑰匙按照某種規律進行分組,每組鑰匙中只需要帶最有特點的一個,當發現某個“特徵鑰匙”差一點就能開鎖了,則當場對該鑰匙進行簡單的打磨,直到能開鎖爲止。這種方法是既省力又省時的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章