今天因某些需要而詳細瞭解了彩虹表的一些細節,才發現其原理比之前想象的更值得稱讚。它所謂的time-memory trade-off,並不是簡單地“以空間換時間”,而是雙向的“交易”,在二者之間達到平衡。
在此分享一些心得。
- 彩虹表的前身
當面對要破解的哈希函數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函數。,因此這兩條哈希鏈能解密的明文數量就遠小於理論上的明文數n。不幸的是,由於集合只保存鏈條的首末節點,因此這樣的重複鏈條並不能被迅速地發現。隨着碰撞的增加,這樣的重複鏈條會逐漸造成嚴重的冗餘和浪費。
- 彩虹表的改進點
彩虹表的使用比哈希鏈集稍微麻煩一些。的首先,假設要破解密文位於任一鏈條的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的表還是不費什麼事的。)
- 彩虹表的防禦
最常用的方法,在其它的答案中也提到了,那就是加鹽(salt),這其實是改變了哈希函數H的形式。由於彩虹表在生成和破解的過程中,都反覆用到了函數H,H如果發生了改變,則已有的彩虹表數據就完全無法使用,必須針對特定的H重新生成,這樣就提高了破解的難度。
防禦彩虹表的另一種方法是提高H函數的計算難度,例如將H定義爲計算一千次MD5後的結果。由於H在算法中的重複性,當單次H函數的計算耗時增加,意味着彩虹表的生成時間會大大的增加,從而也能提高破解的成本。
===========簡化比喻的分隔線==============
如果將哈希後的密文比作一把鎖,暴力破解的方法就是現場製作各種各樣不同齒形的鑰匙,再來嘗試能否開鎖,這樣耗時無疑很長;我以前錯誤理解的“彩虹表”,是事先製作好所有齒形的鑰匙,全部拿過來嘗試開鎖,這樣雖然省去了製作鑰匙的時間,但是後來發現這些鑰匙實在是太多了,沒法全部帶在身上。而真正的彩虹表,是將鑰匙按照某種規律進行分組,每組鑰匙中只需要帶最有特點的一個,當發現某個“特徵鑰匙”差一點就能開鎖了,則當場對該鑰匙進行簡單的打磨,直到能開鎖爲止。這種方法是既省力又省時的。