总的来讲,目前公认比较安全的存储密码方式是PBKDF2, BCrypt 或 SCrypt 算法产生的密码。

历史上密码加密存储经历了如下几个阶段:

1. 单向hash(MD5)

做单向的hash加密,以MD5和sha算法为代表,这类做法比明文直接存储看起来要安全,但是如果在db被攻破的时候,以目前计算机的算力加之黑客的各种技术手段,其实跟明文是差别不大的,我们知道密码学里面破解密码最笨的一种方法是暴力破解,随着目前计算机硬件的发展,带来计算能力的提升,每秒钟上亿次的hash计算已经不是问题,直接暴力破解几乎是分秒的事情,另外与之相近的有一种字典表/彩虹表破解的手段,原理很简单,黑客们会把常见密码以及各种hash算法加密后的字符串整理到一个表中,因为只要是同一明文密码同一hash算法得出的值就是一样的,也就是说黑客不断完善整理的彩虹表可以用来任何MD5加密系统的破解,带着破解的db用户MD5密码那一列信息,直接循环去select 明文 from 字典表/彩虹表 where 密文 = 密码,基本可以破解目前多数人设置的普通密码。

2. hash+盐

早期为了改进单向hash的缺陷,为了让彩虹表失效,引入了盐,盐是随机生成的一个唯一字符串,连在明文密码后增强密码的随机性,然后再做hash得到的加密密文存储在db中,这样一个是相同的密码存在db中的值就不同了,另一个是彩虹表也不会再起作用了。但是同样以目前计算机的算力,暴力破解也是分分钟的事情,因为为了校验密码的原因,我们一般会把这种方法生成的密码和盐一块存在db中,明文+盐 hash= 密码,黑客在拿到盐和加密密码后,只需要用计算机不断去生成明文,然后计算就可以很快破解。这种方法是避免了彩虹表,但是暴力破解还是避免不了。

这种情况类似于目前比特币挖矿,以目前矿机的算力挖矿,加之比特币不断增加hash碰撞难度,暴力破解确实越来越困难,比特币越来越稀有,但是随着量子高性能计算机的发展,未来可能比特币这种hash碰撞的难度也是分秒钟的事。

3. PBKDF2, BCrypt 或 SCrypt 算法

这类算法有一个特点,算法中都有个因子,用于指明计算密码摘要所需要的资源和时间,也就是计算强度。计算强度越大,攻击者建立rainbow table越困难,以至于不可继续。这类算法也可以保证即使计算能力不断提高,只要调整算法中的强度因子,密码仍然不可能被轻易的攻破。

另:第三方密码存储服务商

如果一个系统对于密码存储的要求实在很高,比如说如果用户密码泄露会造成大量现金流失,损失是毁灭性不可逆的,那么可以考虑第三方密码存储服务商。在欧美金融界以及电商界,许多早期的大型银行,信用卡,金融机构的线上密码存储,就使用了比较靠谱的第三方存储服务,一方面是自身去开发维护一套复杂的密码存储系统成本可能比整个线上系统还要高,另一方面为了风险规避以及转移。

1、明文存储(极不推荐)

这种设计思路非常简单,但是缺陷也非常明显,数据库一旦泄露,那么所有用户名和密码都会泄露,后果非常严重。

2、使用MD5或其它算法哈希后存储(较不推荐)

MD5算法的原理可简要的叙述为:MD5码以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。
假如你设置的密码是123456,使用MD5后那数据库中存储的就是4QrcOUm6Wau+VuBX8g+IPg==,当用户登陆的时候,会把用户输入的密码执行MD5后再和数据库就行对比,判断用户身份是否合法,这种加密算法称为散列(哈希)。
但是这种方式使用同一个密码进行哈希得到的密文始终是一样的,考虑到多数人所使用的密码为常见的组合,攻击者可以将所有密码的常见组合进行单向哈希,得到一个密文组合,然后与数据库中的摘要进行比对即可获得对应的密码,这个密文组合也被称为彩虹表(rainbow table)。

3、加盐后哈希存储(较推荐)

通过在密码任意固定位置插入特定的字符串,使其不再是人们常用的组合,让散列后的结果和使用原始密码的散列结果不相符,这种过程称之为“加盐”。用户密码+salt之后,进行哈希散列,再保存到数据库,数据库同时存储MD5值和salt盐值,验证正确性时使用salt进行MD5即可,这样可以有效应对彩虹表破解法。使用加盐,需要注意一下几点:

  • 不能在代码中写死盐,且盐需要有一定的长度(盐写死太简单的话,可能被注册几个账号反推出来)
  • 每一个密码都有独立的盐,并且盐要长一点,比如超过 20 位。(盐太短,加上原始密码太短,容易破解)
  • 最好是随机的值,并且是全球唯一的,意味着全球不可能有现成的彩虹表给你用。

4、使用BCrypt算法加密后存储(非常推荐)

即使是加了盐,密码仍有可能被暴力破解,因此,我们可以采取更慢一点的算法,让黑客破解密码付出更大的代价,甚至迫使他们放弃。BCrypt生而为保存密码设计的算法,相比 MD5要慢很多。BCrypt比MD5慢几十倍,黑客想暴力破解的话,就需要花费几十倍的代价,因此一般情况,建议使用Bcrypt来存储用户的密码。
常见的加密算法MD5只要是相同的salt和原密码,加密后产生的密串都是一致的,而对于BCrypt算法,使用相同的明文,每次生成的新的加密字符串都不一样。

5、Spring Boot中使用jbcrypt对明文进行加密

当然我们也可以使用Spring Security中的BCryptPasswordEncoder进行加密,不过使用方法需要引入Spring Security框架,造成项目臃肿。此外,只要在pom.xml文件中导入Spring Security依赖,如果不进行配置,整个项目的接口请求路径都会被拦截,如果只是想单纯的使用BCrypt算法将密码进行加密,更加轻量且无需配置的jbcrypt是更好的选择。