1 彩虹表引入
在看微众银行开源的WeIdentity规范中的可验证凭证Credential数据结构时,看到有关于防止彩虹表方式反向破解哈希的问题,如下图所示。
在执行选择性披露用户的信息时,是将claim中的其他字段内容进行hash,只披露其中某一条或几条数据给验证方。如果不加盐会有什么后果,下面会给出答案。
{
"claim": {
"age": 1,
"gender": "F",
"id": "did:weid:101:0xd6f4d1215c52ee7e7975ac946a0e094040aa5eeb",
"name": "1"
},
"proof": {
"salt": {//对应claim加的盐值
"age": "IObiF",
"gender": "GdzQs",
"id": "dDIIt",
"name": "PecuG"
},
},
}
注意:仅摘取了Credential结构的与本次内容相关的部分内容。
2 存储密码的方式以及安全分析
存储方式 | 安全性 |
---|---|
明文m存储 | 无安全性可言 |
存储明文的哈希值hash(m) | 由于彩虹表的存证,很容易批量还原出密码明文m |
存储密码明文的加盐哈希 hash(m+salt) | salt可以是用户名、手机号等,但必须保证每个用户的salt都不一样才相对安全,盐值不是秘密的,可以随机生成并与密码哈希一起存储。 |
从1976年开始,业界开始使用Cryptographic Hash加密用户密码,最早见于Unix Crypt。但MD5、SHA-1已被破解,不适合再用来保存密码。
例如,保存用户密码的SHA256值,这样就安全了吗?不行。黑客可以用查询表或彩虹表来破解用户密码。注意是破解密码不是破解sha256,能根据sha256破解密码的原因是,
用户密码往往需要大脑记忆、手工输入,所以不会太复杂,往往具有有限的长度、确定的取值空间。
-
短的取值简单的密码可以用查询表破解
比如8位数字密码,一共只有10^8种可能。一亿条数据并不算多,黑客可以提前吧0-99999999的sha256都计算好,
并以sha256做key密码为value存储为一个查询表,当给定sha256需要破解时,从表中查询即可。 -
取值相对复杂,且长度较长的密码,可以用彩虹表破解
比如10位,允许数字、字母大小写的密码,一共有(10+26+26)^10种可能,记录非常之多难以用查询表全部保存起来。这时候黑客会用一种叫做彩虹表的技术来破解,彩虹表用了典型的计算机世界里解决问题的思路,时间换空间。在这个例子里面,空间不够,那就多花一些时间。在彩虹表中,可以将全部的sha256值转化为长度相同的若干条Hash链,只保存Hash链的头和尾,在破解的时候先查询得到sha256存在于哪条hash链中,然后计算这一条hash链上的所有sha256,通过时比对来破解用户密码。
3 彩虹表的前身
彩虹表之前已经出现了对hash函数的破解算法,被称为:“预计算的哈希链集”(Precomputed hash chains)。下面是k=2的哈希链:
KaTeX parse error: Undefined control sequence: \require at position 2: \̲r̲e̲q̲u̲i̲r̲e̲{AMScd} \begin{…
不变的R函数会存证:哈希碰撞问题
在哈希链集中,每次对哈希结果逆向计算时,采用的R函数是一样的,因此会产生哈希碰撞。随着哈希碰撞的增加,这样的重复链条会造成严重的冗余、存储空间和破解时间的浪费。
KaTeX parse error: Undefined control sequence: \require at position 2: \̲r̲e̲q̲u̲i̲r̲e̲{AMScd} \begin{…
KaTeX parse error: Undefined control sequence: \require at position 2: \̲r̲e̲q̲u̲i̲r̲e̲{AMScd} \begin{…
因此,在选择函数R时,非常重要。更详细的解释,可参考知乎问答[1]。
4 彩虹表
上图一展示了一个Hash链长度为3的彩虹表,因为在Hash链中需要将Hash值使用R函数映射回密码取值空间,为了降低R函数的冲突概率,长度为K的Hash链中,彩虹表会使用k个R函数,因为每次迭代映射回密码空间使用的R函数不一样,这种破解方法被称作彩虹表攻击。
网络[2]上已有一些已经计算好的彩虹表可以直接使用,所以直接保存用户密码的sha256是非常不安全的。
如果已经有了上图一生成的彩虹表,怎样找到Hash函数H的一条密文re3xes对应的明文呢?
实现这个,需要两个步骤,第一步猜测密文属于哪一条链;第二步验证猜测,同时计算出对应的明文。
解释这个破解过程需要明确一点:如果re3xes对应的明文属于彩虹表中的某条链,那么就有可能找到其对应的明文,注意这里的”属于某条链“不仅仅是指属于彩虹表的一条链中存放的头尾两个字符串,还包括这两个字符串中的中间数据,图一中中间计算的明文数据secret、jimbo也算是属于彩虹表的第一条链中,同理bernie、zurich属于第二条链,culture、crypto属于最后一条链,虽然彩虹表中只保存了每条链的链首链尾两个字符串,但是这些中间数据是可以根据链首字符串重新计算出来的。
图二展示了re3xes的破解过程。
先猜测下密文re3xes对应的明文数据是某条链中间计算出数据的最后一个,注意第一、二条链的中间数据中的最后一个明文口令jimbo、zurich,依次经过H-R3运算得到保存的链尾字符串rootroot、myname,那么密文re3xes经过R2-H-R3转换之后得到的数据就是某条链的链尾字符串,这点应该不难理解。
如密文v0d$x
对应的明文jimbo是第一条链最后一个中间明文数据,则v0d$x
经过R3转换得到链尾字符串rootroot,但是密文re3xes经过R3函数转换之后得到的rambo并不是表中保存的任一条链的链尾字符串,这就说明re3xes对应的明文数据并不是某条链中间计算出数据的最后一个,猜测不成立。
继续猜测re3xes对应的明文数据可能是某条链中间计算出数据的倒数第二个,同样可以很容易推出re3xes依次经过R2-H-R3转换之后得到的数据是某条链的链尾字符串,计算出re3xes经R2-H-R3转换的结果为linux23,通过搜索彩虹中存放的链尾字符串,得到linux23恰好是最后一条链的链尾,到了这一步已经成功了一大半。
下面就来根据存储的最后一条链链首的passwd重新计算出密文re3xes对应的明文,进行验证阶段。既然re3xes经R2-H-R3转换之后得到链尾的linux23,那么链首的passwd经H-R1-H运算后的结果culture就是re3xes对应的明文。
5 彩虹表攻击的防御
加盐值,一般来说就是将用户的key+salt一起Hash,即Hash(key+salt),然后与salt一起保存。
S a l t e d h a s h ( p a s s