密码加密方案进化史

最早我们使用类似 SHA-256 这样的单向 Hash 算法。用户注册成功后,保存在数据库中的不再是用户的明文密码,而是经过 SHA-256 加密计算的一个字符串,当用户进行登录时,将用户输入的明文密码用 SHA-256 进行加密,加密完成之后,再和存储在数据库中的密码进行比对,进而确定用户登录信息是否有效。如果系统遭遇攻击,最多也只是存储在数据库中的密文被泄漏。

这样就绝对安全了吗?当然不是的。彩虹表是一个用于加密 Hash 函数逆运算的表,通常用于破解加密过的 Hash 字符串。为了降低彩虹表对系统安全性的影响,人们又发明了密码加 “盐”,之前是直接将密码作为明文进行加密,现在再添加一个随机数(即盐)和密码明文混合在一起进行加密,这样即使密码明文相同,生成的加密字符串也是不同的。当然,这个随机数也需要以明文形式和密码一起存诸在数据库中。当用户需要登录时,拿到用户输入的明文密码和存储在数据库中的盐一起进行 Hash 运算,再将运算结果和存诸在数据库中的密文进行比较,进而确定用户的登录信息是否有效。

密码加盐之后,彩虹表的作用就大打折扣了,因为唯一的盐和明文密码总会生成唯一的 Hash 字符。

然而,随着计算机硬件的发展,每秒执行数十亿次 Hash 计算已经变得轻轻松松,这意味着即使给密码加密加盐也不再安全。

在 Spring Security 中,我们现在是用一种自适应单向函数(Adaptive One-way Functions)来处理密码问题,这种自适应单同函数在进行密码匹配时,会有意占用大量系统资源(例如 CPU、内存等),这样可以增加恶意用户攻击系统的难度。在 Spring Security 中,开发者可以通过 bcrypt、PBKDF2、scrypt 以及 argon2 来体验这种自适应单向函数加密。

由于自适应单向函数有意占用大量系统资源,因此每个登录认证请求都会大大降低应用程序的性能,但是 Spring Security 不会采取任何措施来提高密码验证速度,因为它正是通过这种方式来增强系统的安全性。当然,开发者也可以将用户名/密码这种长期凭证兑换为短期凭证,如会话、OAth2 令牌等,这样既可以快速验证用户凭证信息,又不会损失系统的安全性。