文中关于彩虹表的描述有误,评论里比我讲得更准确:
Lu Jerry1 分钟前
关于彩虹表的描述是错误的,这是暴力穷举表,建议答主修改答案。原文“这彩虹表大概多大呢? 答案是, 10TB 的硬盘需要……”修改成“10TB 的硬盘需要一个”。
但凡看了wikipedia, 都不会弄出来这样的问题. 请用 bcrypt/Argon2的较新版本/Scrypt.
我接下来的回答主要针对有些人不明白为什么salt要足够长.
好多人不懂为啥要增加salt的长度, 尤其是 @Ivony 评论底下的.
(建议你们还是直接用相应语言的bcrypt官方demo吧.... Go的bcrypt库十分友好, 知道你不会加盐, 加盐的步骤直接在内部帮你包装好了...【手动狗头】)
我们举个理论模型, 现在用户密码是4位的大小写数字字母符号组合(26大写, 26小写, 10数字, 18符号, 总计80个), 加密方式SHA1(8 bytes), 那么彩虹表有多大呢? 是80^4 x 8bytes = 312.5MiB. SD卡都能装得下.
这时如果我们给用户密码再来个4位的跟密码一样空间的salt, 那么需要的彩虹表会是多大呢? 80^4 x 80^4 x 8bytes = 11.92 EiB 这彩虹表大概多大呢? 答案是, 10TB 的硬盘需要 1300 多块才能装得下, 假设一个 2U 机器装 12 块硬盘, 这需要 6 个机柜(42U计算).
有同学会说了, 那我让用户直接来一个长密码, 16位! 这需要 2.25 × 10^31 byte! 总没问题了吧? 你觉得用户会记得住一个长达16位的密码吗? 他肯定会用手机号/生日随便再来点什么对吧. 那对应的彩虹表也会相当的小. (用户密码熵不可置信问题)
那么有了salt就可以了吗? 不可以, salt也要足够长/足够复杂. 这样才能达到增加 hash target 的信息量(也就是熵)的目的. 这样才会让彩虹表的制作足够难.
设想一个极端场景, 数据库被"拖库"了, 不但hash结果, 甚至salt都已经是已知的了, 源代码也被泄露了, 散列方法无论是 bcrypt 还是 Argon2 还是 Scrypt 人家都知道了, 甚至你的库进行着定期的rehash, 人家连过去的版本也搞到了(可能进行特征/侧信道攻击), 我们该如何保护用户的密码原文?
那就只有salt的熵了(期望用户密码拥有足够的熵不靠谱). 而提升salt的长度可以有效提升存储熵的空间. 这就是 @Ivony 回答论述的主要内容.
所以正确的做法是, 要求用户的密码有足够的熵(复杂度检测, 不通过让他换密码). 以及salt也要有足够的熵. 然后请用相应语言的密码学库里面的散列方法. 或者干脆, 用脑子记住用bcrypt.
试想一下 cracker 去 StackOverflow 问: "各位大佬, 求一个1024bit长度的彩虹表", 会不会被人笑爆然后问, 怎么? 你捡到外星U盘还是外星电脑了? :)
大师 Bruce Schneier 有句话, 被称作 Schneier's Law:
"Anyone, from the most clueless amateur to the best cryptographer, can create an algorithm that he himself can’t break."
人家大师就是大师, 不会直接折了你面子, 但其实意思就是, 别自己发明加密算法.
- 真正安全的算法是久经社区考验的, bcrypt已经22年了(1999-2021), Scrypt也12年了(2009-2021).
- 而且是要社区流行的. 光自己看的懂没意义, 别人也能用才有意义, 才能成为标准.
Schneier 对此又说了:
"There are two kinds of cryptography in this world: cryptography that will stop your kid sister from reading your files, and cryptography that will stop major governments from reading your files."
(世界上有两种密码学: 一种是阻止你妹妹阅读你的文件的密码学, 另一种是阻止强大的政府阅读你文件的密码学.)
最后.
建议多读书, 书上写的很清楚:
"If the salt is big enough, it essentially makes dictionary attacks infeasible."
Secure Programming Cookbook for C and C++
延伸阅读, 现在已经不太建议用PBKDF2了(因为有FPGA/ASIC), 应该转向其他的 GPU/内存/ASIC 抗性更强的散列方法:
https:// medium.com/analytics-vi dhya/password-hashing-pbkdf2-scrypt-bcrypt-and-argon2-e25aaf41598e
没有意义,早就有md5+sha1的彩虹表了。
至于说加盐对抗彩虹表的人,也绝大多数不明白这里面的原理和逻辑。
加盐的确是对抗彩虹表,但是到底是怎么对抗的,其实很多人仍然是一知半解。要搞清楚这一点,就要搞清楚彩虹表的原理。
彩虹表很简单,就是预先计算出所有可能密码的哈希值,然后破解的时候查表好了。
彩虹表的方式理论上无法防御(这与加密算法不同),只要是有限长度的密码,就可以被彩虹表攻击。并且彩虹表可以复用,所以计算单次密码攻防成本也是扯淡,随着时间的推移,彩虹表只会增长不会收缩。
所以对抗彩虹表只有一个方法,这也是绝大多数人压根儿没有弄明白的事情,彩虹表最大的敌人是密码的可能性,更进一步的如果考虑到彩虹表的无限增长的问题,所以彩虹表的唯一防御手段是增加密码长度!
所以加盐的本质目的是:增加密码长度。
密码长度的增加,破解该密码的彩虹表的大小需要呈指数增长,这才是防御彩虹表的根本原因。
所以加盐是必须的,但是你加的盐太短了,把你的盐当作密码的一部分去查表就完了……
其实说白了吧,如果你只是要防御彩虹表,仅仅针对彩虹表防御,你只需要把你的密码拉到特别长,彩虹表直接失效,有没有盐都失效了……
更进一步的,如果你的密码长度足够长,长到比哈希值的长度还要多出不少。这时候即便别人找到了一个碰撞,那大概率也不是你的密码了,你密码的安全可以得到更彻底的保护……
最后再补充一点好了,彩虹表破解的是你的密码,他的依据是大部分人在不同的网站会使用同一个密码,所以得到你的密码,就能入侵别的网站。对于已经侵入后台拿到你密码哈希值的黑客来说,再去伪造你的身份已经没啥意义了,因为他既然都已经侵入了网站的后台,在这个网站想干点儿啥还需要你那密码么?
看来你不知道什么是salt。生成一个随机数,我们称之为salt,然后在数据库中记录salt和h=hash(pwd + salt),查询的时候,得到用户的口令p,然后从数据库中查出salt,计算hash(p+salt),看是不是等于h,等于就是对的,不等于就是不对的。
单纯使用MD5之所以不好,并不是说MD5这种方法容易遭到破解,而事实上对于MD5求原象或者第二原象,也就是“逆计算”这种破解,没有什么很好的方法。只能通过预先计算知道许多MD5的对应关系,存在数据库中,然后使用的时候反查,例如我知道'password'的MD5值是5f4dcc3b5aa765d61d8327deb882cf99,那么我就用一个数据库存起来,只要我看到5f4dcc3b5aa765d61d8327deb882cf99,我就知道这个是口令'password‘使用MD5处理之后的值,原来的口令就是'password'。MD5在身份鉴别系统中用于口令保护已经是很久了事情了,大部分黑客也有针对这种Hash方式准备相应的数据库进行反查,这种数据库称为彩虹表。
所以,为了对抗彩虹表,我们要做的工作是避免预先计算,让攻击者无法(或者非常困难)提前计算好彩虹表。
为了反映为何彩虹表计算是可行的,我们再来算一下。我们假设用户可能输入的口令是键盘上的小写字母和数字,共26+10=36种,之所以这样假设是因为 一个用户比较多的系统中总是会有一些弱口令用户的,我们假设输入的口令至少5个字符,至多12个字符,那么用户可能的输入一共有:
,而12个字节可能的组合应有
种。如果再考虑到用户为了方便记忆,输入的口令是一些已经存在的单词或是词组,可能的输入将会远远少于。用户可能的输入少了,就给了我们枚举的空间。
为了阻止这种枚举,加salt的方法是扩大用户输入的一种简单有效的途径,随机生成一个16字节的随机数,加上用户本身输入的至多12个字符的口令,可能的输入就有种,这么多种可能性,任何一个机构和组织都没有办法存储规模如此庞大的彩虹表。
另外一种方法是通过提升Hash的复杂度,延长攻击者进行暴力破解时所消耗的时间。现在显卡用于并行计算实在太容易,6位纯数字的口令在显卡看来就是秒破。Hash算法的多次迭代就是最简单的延长计算时间的方法,Apache的htpasswd就使用了MD5的1000次迭代,不过只是使得这些口令稍微难破解一些。
另外,题中使用了SHA1和MD5两种算法的方法,除了稍微提升一点计算的难度以外,并没有多好,这种组合方法不能增加用户输入的可能性,另外虽然SHA1生成的是160位的Hash,但是由于输入是一个128位的MD5,所以输出也至多只可能有种可能,猜测的范围也没有缩小。所以这是原来回答我建议你使用更多次数(如1000次)MD5迭代的原因,至少应当有一个方面有稍微大一些的加强。
另外
此文(暴力密码破解器 ocl-Hashcat-plus 支持每秒猜测最多 80 亿个密码,意味着什么?)中有数据可以供参考,bcrypt是一种有效对抗口令Hash破解的算法,建议使用。