前言
用户信息泄露事件层出不穷,百度或谷歌输入“密码泄露”,搜出来的泄密门更是让人目瞪口呆:从小公司到大公司,从明文存储到普通的哈希加密。作为一个IT从业者,我深刻感受到“得用户者得天下”,尤其在互联网+盛行的趋势下。密码存储作为软件服务系统基础架构中不可缺少的一部分,越来越多的受到开发者的重视。对于一个服务,如果信息安全部分出问题,我想没有必要进一步去做用户体验的提升,性能的优化。进而也不可能获取大用户的青睐。还是那句话:出来混,迟早要还的。
本人并非密码学出生,这篇文章不是用来介绍各种加密算法本身。写这篇文章的目的,结合工作项目中一次大规模密码加密迁移的亲身体验,主要是介绍软件开发中常用的一些密码存储方法。
1. 用户信息认证
有系统的地方就有用户信息认证服务。用户信息认证也经历了一开始的每个系统提供自己的用户信息认证,到多个系统采用统一的用户信息认证(如LDAP),再到目前比较流行的第三方授权认证(如OAuth 2.0授权开放网络标准)。
用户信息的认证一个端到端的过程,从安全的角度看,更是一个端到端的安全认证过程:
- 前端用户信息输入安全性:如密码的长度要求,特殊字符组合要求,近期相似性匹配要求等
- 传输端用户信息安全性:如https传输过程中会用到对称加密,非对称加密和哈希算法等各种手段尽可能多的保证数据是传输是安全的
- 后端用户信息存储安全性:如用户密码在后端数据库中采用md5, sha1, sha256, bcrypt等存储
本文浅谈“后端用户信息存储安全性”中的敏感信息密码存储安全。
2. 密码加密
2.1 哈希算法
常用的一些处理密码加密的算法有MD5, SHA1, SHA256, SHA512等。这些加密算法具有不可逆的特点(不能从密文反推出明文),具有等幂性(同样的明文多次哈希得到相同的值)。输入的微小变化会产生完全不相似的密文。算法计算速度快。一些典型的密码泄密事件中,我们发现,很多就是采用这类算法进行密码加密的。黑客常采用字典法或暴力法破解。
单一哈希算法的密码处理比较简单,有些系统于是采用多次哈希处理:如md5(sha1(password)+md5(password))等。
2.2 加盐(salt)
盐是什么?
盐是一个随机生成的字符串。由于采用上面哈希算法对密码明文处理过于简单。于是对密码明文加上盐,然后再用哈希算法处理。盐可以加载明文的前面或者后面,或者根据一定的算法加在明文的不同的位置。由于对用户的密码采用随机生成的盐处理,因此相同的明文也会产生不同的密文。从黑客攻击的角度上,进一步加大的破解的难度。
盐如何存储?
对于加盐哈希算法,盐一般也作为一个字段存储在数据表里。而对于安全性更好一点的算法,盐也可以隐藏在密文里,如bcrypt。
3. 密码破译
通过2章节,我们了解到一般情况下,密码加密由两部分构成:加密算法和盐。而对于密码破译,一般常用的方法有:
- 字典法:建立一个密码明文字典,对于字典里的明文密码一一匹配
- 暴力法:穷举一定长度的明文密码
- 查表法:建立密码密文哈希表,然后对表查密文
- 逆向查表法:哈希不同的密码明文,匹配具有该密码明文的用户
- ...
深入了解这些常用的破译方法,请参考:https://crackstation.net/hashing-security.htm
其实不管采用何种方法对密码加密,在足够长的时间里,都是可以破译的。因此密码存储安全,只有相对的安全。安全的代价是性能。
我们来看个例子(来自网上数据):
假设密码由小写字母和数字组成。对于一个长度为6的密码采用MD5加密,根据计算机的计算性能不同,采用暴力破解一般在几秒和几十秒之间。但如果采用bcrypt(work factor设为12)加密,采用暴力破解则需12年左右。
到这里,我们可以看到,为什么密码加密强力推荐采用bcrypt,加密算法不慢不快(可以通过factor进行调节),如果采用上述密码破译方法进行破译,需要付出时间代价。相对来说安全。而普通的哈希算法,加密速度太快。对于一台现代计算能力强的计算机来说,简单的密码非常容易暴力破解。
4. 密码迁移过程
这里密码算法迁移是指对于线上系统,从一种已有的密码加密算法迁移到另外一种密码加密算法。
密码迁移过程,结合实际项目,介绍一种密码迁移的方法,适用于:对于一个已经庞大的线上系统,密码存储采用相对不太安全的加盐的哈希算法。现在的需求是要把密码存储策略改成相对更安全的bcrypt算法。
- 数据表前期处理:我们会在密文数据表中加入两个字段(如hashfunc, bcrypthash)分别表示加密算法类型和加密后的密文,注意假设表中原来的密文以字段passwordhash存储。
- 老用户处理:第一轮我们会对线上数据表中做一轮迁移,把老用户的passwordhash迁移到bcrypthash里。迁移的方法可以采用bcrypt(passwordhash)进行,因为我们无法得知密码的明文。只能依托密文进一步再加密来填充bcrypthash。
- 新用户和更新用户处理:对于能够创建用户,更新用户信息和验证用户信息的组件,需要相应的改动。新用户创建和更新方面,直接按照bcrypt(password)产生密文填充bcrypthash。用户信息验证方面,根据hashfunc的类别,进行相应的计算。
- 数据表后期处理:最后我们会对数据表再做一轮迁移,把不再需要的字段passwordhash删掉。
整个迁移过程并不复杂,难点在于流程控制,除了数据库本身,具有用户信息处理的组件可能也要做相应的处理。整个迁移的过程中,我们需要保证,任何阶段,对于老用户,新用户,更新密码的用户都可以正常工作。
总结
本文浅谈密码存储安全中的一些常识。正是因为密码存储安全是相对的,所以我们在实际项目中采取何种算法加密应该根据实际需要,相对的安全也是建立在性能之上。