彩虹表

以前我也跟其他很多人一样,认为彩虹表就是描述“明文->密文”对应关系的一个大型数据库,破解时通过密文直接反查明文。
今天因某些需要而详细了解了彩虹表的一些细节,才发现其原理比之前想象的更值得称赞。它所谓的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函数,而是分别使用R 1…Rk 共k个不同的R函数。这样生成的哈希链集即被称为彩虹表。(在不同的运算位置使用不同的R函数,就像彩虹由内而外的不同位置上显示出不同的颜色一样。)这样一来,如果发生碰撞, 当两个链条发生碰撞的位置并非相同的序列位置时,后续的R函数的不一致使得链条的后续部分也不相同,从而最大程度地减小了链条中的重复节点,保证了链条的有效性。
彩虹表的使用比哈希链集稍微麻烦一些。的 首先,假设要破解 密文位于任一链条的k-1位置处,对其进行R k 运算,看是否能够在末节点中找到对应的值。如果找到,则可以如前所述,使用起节点验证其正确性。否则,需要继续假设密文位于k-2位置处,这时就需要进行R k-1 、H、R k 两步运算,然后在末节点中查找结果。如是反复,最不利条件下需要将密文进行完整的R 1 、H、…R k 运算后,才能得知密文是否存在于彩虹表之中。

  • 时间、空间的平衡
通过彩虹表的使用方法可以明显地看出,一条哈希链实际代表的是一组明文的解密规则:
同时,对于相同个数的明文,当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函数的计算耗时增加,意味着彩虹表的生成时间会大大的增加,从而也能提高破解的成本。

===========简化比喻的分隔线==============
如果将哈希后的密文比作一把锁,暴力破解的方法就是现场制作各种各样不同齿形的钥匙,再来尝试能否开锁,这样耗时无疑很长;我以前错误理解的“彩虹表”,是事先制作好所有齿形的钥匙,全部拿过来尝试开锁,这样虽然省去了制作钥匙的时间,但是后来发现这些钥匙实在是太多了,没法全部带在身上。而真正的彩虹表,是将钥匙按照某种规律进行分组,每组钥匙中只需要带最有特点的一个,当发现某个“特征钥匙”差一点就能开锁了,则当场对该钥匙进行简单的打磨,直到能开锁为止。这种方法是既省力又省时的。

你可能感兴趣的:(旁门左道)