最近做了一些与密码学相关的工作,对于消息的加解密有了一些新的看法。结合之前项目中遇到的服务端密码存储的问题,在这里写一点自己的想法。
目前常见的一些密码存储方式
以下是一些常见的密码存储方式,其中一部分是自己用过的,一部分是见过别人使用的:
- 明文
pwd
- 经过MD5 哈希后存储
md5(pwd)
- 两次MD5后存储
md5(md5(pwd))
- 加盐md5存储
md5(pwd + salt)
- 密码扩展后存储
kdf(pwd)
- 慢哈希后存储
brypt(pwd)
- 以上多种算法组合后存储
哈希
很明显,第 1 种是最不安全的。存储在数据库中的密码可以轻易地被管理员看到。一旦服务器被拖库,这些密码就轻易地被别人窃取,并可以根据账号和密码在其它网站上试探(大部分用户在多个网站上使用相同的用户名和密码)。尽管人人都知道这种存储方式是极不安全的,但仍然有很多网站使用它。如前几年震惊中外的 CSDN 拖库事件 。在这一事件中,有600万用户的信息被泄漏。
第 2 种 方式比较古老,管理员和黑客无法看到用户的明文密码。但如我们所知,MD5 是不安全的。如 MD5 碰撞算法。而在此之前,查表法一直做为破解 MD5 密码的重要手段 。 所谓 查表法
,就是对字典(密码集) 进行 MD5 运算,将哈希值预存储在一个或多个表里。
当需要破解某密码时,根据服务器存储的哈希值在预计算的哈希值表中查找对应的值。而 反向查表法
,攻击者可以同时对多个重合密码进行攻击。
1 2 |
SELECT table_b.pwd, table_user.uname FROM table_user,table_b WHERE table_b.hash_val = 'hash_val_1' ; -- 查表 SELECT table_b.pwd, table_user.uname FROM table_user,table_b WHERE table_user.hash_val = 'hash_val_x' ;. --反向查表 |
如果用户使用了弱密码,这种使用反向查表法来破解密码简直不要太轻松。
使用查表法需要存储字典与哈希值,对存储空间有较高的要求。后来进化出了 彩虹表
法,在算法的空间和时间上进行了优化。
加盐
第 3、4 种方式针对上述攻击方法做了改进。其实第 3 种方式并大的改进, 因为算法和参数是固定的。而第 4 种的改进比较好。所谓 加盐(salt)
,即在消息的任意固定位置添加附加消息。它使攻击者的字典变得更加复杂,攻击者计算预存储值的难度大大增加了;每个密码都混入了不同的盐,所以使得反向查表法去批量匹配密码变得难以施行。在加盐存储的实践中,有部分人使用了错误的实现:如盐值过短,或盐值重复。
盐值过短
无法对攻击者造成足够的困扰,一个好的盐值的长度起码要和哈希值的长度一至。而 盐值重复
则和未加盐没有区别:两个相同的密码加相同的盐,得到的哈希值是一样的。特别是对于那些将盐值硬编码到代码里的,简直是在为攻击者提供帮助。类似地,也不要使用用户名、用户id、创建时间等字段做为盐值--盐值应该是随机的,且并用户修改密码时应该给出新的盐值。
继续阅读