背景
最近在做一个md5撞库比对的功能,本来想用穷举法暴力撞库的,但遭遇瓶颈。情况是这样的:
数据库表中有3个字段,id,mobile,md5_mobile。要存40亿数据算了下 大概需要接近1TB的数据库容量,以及 30天的入库时间。时间和空间都不允许,怎么办。看了下网上说的彩虹表算法来破解md5,了解下来有了一点自己的理解。
预计算的哈希链
了解彩虹表前先了解 一个概念:预计算的哈希链
预计算的哈希链 涉及以下这几个定义
- H函数:hash函数:用于将明文转为密文(加密函数)
- R函数:自定义逆hash函数:用于将密文转为明文
R函数
R函数可以是一个自定义的伪逆hash函数。什么意思?
假设明文是:bixie,密文是 hicxvhiniojjghg
就是R函数是一个我们自定义的函数,用于将密文转为明文,比如将 hicxvhiniojjghg这个密文转为一个明文 hic。
哈希链
在此处我把R函数自定义为 截取字符串前3位(类似substring函数)
此时我们通过一个 明文 bixie 经过【三轮】 H函数 和 R函数 得到了一个值 vbj
上图就是一个hash链,经过3轮(同时把3轮中的这个3定义为K) H、R 函数的计算,在hash链的末尾可以看到,我们得到了一个vbj的值。
我们把这个链的首尾值(bixie、vbj)先存到表中,并记下这个 轮数 K=3。
预计算
情况1:末位匹配
假设我们现在需要破解一个密文:vbjyuqwwejomihbn
我们先在程序中用R函数得出一个结果:vbj,然后去表中根据尾值查询,刚好能查到 bixie->vbj 这个键值对,说明密文大概率处于 bixie->vbj 这条链上。
那我们从bixie这个字符开始重新生成 hash链,可以在 RK 这个位置查到密文,Rk-1这个位置查到明文,破解成功,明文是 loi。
情况2:末位不匹配
假设我们现在需要破解一个密文:loiasdhuibqwebx
我们先在程序中用R函数得出一个结果:loi,然后去表中根据尾值查询,此时是查不到数据的。
但结合上面的hash链,在上帝视角,我们可以看到 loi确实是处于 bixie->vbj 这条链上的,并且是在R2的位置。
但当局者迷,程序本身是不知道这个信息的,那我们需要帮助它,让它从loi转为vbj,然后再去表中根据尾值查询。
此时我们对loi 再进行一轮 H函数和R函数的运算,就得到了 vbj,这个时候就能在表中找到这个bixie这个初始参数了,找到之后根据首位值重新生成 hash链 可以在R1的位置找到明文了。
------------------------------------------------------分割线---------------------------------------------------------
假设我们现在需要破解一个密文:hicxvhiniojjghg(此时密文处于H1的位置)
我们先在程序中用R函数得出一个结果:hic,然后去表中根据尾值查询,此时是查不到数据的。
此时我们对hic进行一轮 H函数和R函数的运算,就得到了 loi,但loi还是不能匹配,那么就再对loi 进行一次 H、R函数,即 对对hic进行两轮 H函数和R函数的运算,就得到了 vbj。
然后重复上面的生成操作,就能找到明文是初始值 bixie。
分析1:R函数的次数
在上面的两种情况中,可以看到密文都破解成功了,但是情况2中有一个点是要引起注意的,即如果末位不匹配,到底需要计算多少轮呢,总不能一直循环下去吧。
结合上面的例子,其实很容易发现,我们的 hash链 bixie->vbj是经历了3轮H、R函数的运算 的。那么即使密文处于第一个位置,即 H1,那么它最多也只需要 3轮计算就能得出 尾值 vbj。
如果计算超过3轮计算的值依旧匹配不上这条hash链的尾值,说明明文就不在这条链上,那么就该放弃这条链去匹配其他的hash链了,此时做超过3轮的计算已经没有意义了。
所以说 密文进行 H、 R函数最多就是三轮,即上文记下的 K=3(K=3)。换言之,链长为K,最多进行K次H、R函数计算,就可以匹配到尾值从而找到链进行比对。
进行的K次H、R函数计算的过程,就是我理解的预计算的概念(预先计算好尾值,再去匹配)
分析2:时空的平衡
在很多有关彩虹表的说明中,都说 与彩虹表算法是时空平衡的算法, 与之对应的两个极端是穷举法 与字典法
此时我们有一个密文 AAA,想要通过撞库得到一个明文
- 穷举法:先生成n个明文,遍历所有明文,每一次遍历得到一个密文,将密文与AAA进行对比,一致则把明文返回出来。此法需要耗费极长的时间,才能获取到明文,
- 字典法:将穷举法遍历的所有明文和与之对应的密文全都存到表中。这样查询的 时候就不需要再穷举了,可以根据密文快速检索出明文。此法需要占用极大的存储空间,才能将所有的明文和密文都存下来
预计算的哈希链:在上文中我们提到,链长为K,最多进行K次R函数,就可以匹配到尾值,那如果K=1呢,K=1的时候,初始值就只进行了一次 H、R运算。首值与尾值 是一对一的,那么数据库中需要存的链 就跟字典法要存的链一样多了
那么就可以得到一个结论:
- 当链长越长,所需要计算 R函数的次数就越多,耗时越长,数据库中存的链越少,所占存储就越少
- 当链长越短,所需要计算 R函数的次数就越少,耗时越少,数据库中存的链越多,所占存储就越多
- 一条链有K个H函数的结果,那么这条链上 能破解的密文个数等于K
只要找到一个合适的链长 K 值,那么时间和空间的 占用就可以达到一个折中平衡,时空互补的状态。
预计算的哈希链的缺点
上面的算法我称之为 哈希链的预计算 算法,此算法有不足。
就是 H函数每次生成的值是加密后的,但是R函数是我自定义的逆加密函数,用来生成跟明文同格式的字符。
这就产生了一个问题,当一个链足够长的时候,R函数有极大概率生成 重复的值,甚至生成初始值。如下
第一条链
第二条链
通过上面的两条链,可以很明显的看出来
- 第二条链包含了第一条链的部分节点,如上,假如密文是 bbb111,那么第一、二两条链都可以破解这个密文,这样就形成了冗余
- 一条链原本能破解K个密文,但出现了重复,那么单条链能破解的个数可能就远小于K个
- 由于只保存链条的首末节点,那么重复链条并不能被迅速地发现,也不能迅速的修正
彩虹表
说了那么多预计算hash链的事儿,那么彩虹表是什么呢。
彩虹表的破解原理就是基于hash链的预计算,只不过是对上文的R函数进行了优化。
上文的R函数是一个我自定义的伪函数,在每一轮循环中,这个伪函数都是相同的,做一个相同的操作。
引用不知道从哪看到的一段话:
在构造哈希链的时候,一个优秀的函数R功不可没。首先R需要能将值域限定在固定的范围------例如给定的长度范围、给定的字符取值范围等等------之内,否则的话,哈希链中大量的计算结果并不在可接受的取值范围内,一条链条无法对应多个明文,链条就失去了意义;其次R必须同哈希函数一样,尽量保证输出值在值域中的均匀分布,减少碰撞的概率
定义
彩虹表的优秀就在于对R函数的改造:
每一轮的循环(循环 指 重复进行 H、R函数计算)中,R函数是不同的。有K个循环,就有 从R1 、R2直到 RK个 一共K个不同的R函数
那么彩虹表为什么叫彩虹表就可以理解了,在一条类似彩虹的链条上,每一个节点上的R函数都不同,像七彩斑斓的色彩一样分布,所以称之为彩虹表。
改进的R函数
彩虹表改进的R函数,可以达到以下效果:
第一条链(链1)
第二条链(链2)
第三条链(链3:丢弃)
在上图的三条链中:
链2的 R1、H2位置分布着跟 链1的 R2、H3位置相同的值,但由于 链2的R2跟 链1的R3是不同的函数,链2 R2的值是 kkk ,链1 R3的值是 hhh,所以后续位置的值是不会重复的。
链3的 R2、H3的值和链1的 R2、H3的值相同,R函数出现的位置也相同,这是极端情况。那么 链3 这条链就会被丢弃掉。
总结来说,彩虹表算法可以改进这几个地方
- 同一条hash链上 不会生成重复值
- 不同的hash链上 不同节点生成重复值后,由于R函数的不同生成新值,不会一直重复
- 不同的hash链上 相同节点生成重复值后,由于R函数相同生成重复的值,那么这条链会被丢弃掉
使用
网上有很多使用的教程,本篇只探究原理,使用就不细说了,普遍是 下载别人生成好的 彩虹表,并下载 彩虹表计算软件或源码,输入 密文,运算得到明文。
彩虹表的不足
彩虹表无法破解加盐的 hash值。
在上述的例子中,从 预计算的hash链 到彩虹表,改变的只是 R函数。
如果用户加盐了呢,那么针对每一个用户 ,H函数 得出来的结果都是不同,我们想要破解,需要针对每一个加盐用户生成 不同的 H 值。
换言之,每多一个用户,就要多生成 一个彩虹表,这是不现实的。由此也可以受到启发,当我们需要做加密时,加盐可以有效防止密码被撞库破解。
特此记录,以备后用...