上一篇博客:LeetCode 2529. 正整数和负整数的最大计数
写在前面:大家好!我是
晴空๓
。如果博客中有不足或者的错误的地方欢迎在评论区或者私信我指正,感谢大家的不吝赐教。我的唯一博客更新地址是:https://ac-fun.blog.csdn.net/。非常感谢大家的支持。一起加油,冲鸭!
用知识改变命运,用知识成就未来!加油 (ง •̀o•́)ง (ง •̀o•́)ง
简介
今天看到了一个挺有意思的面试题:为什么忘记密码要重置密码而不是直接告诉你密码? 不知道大家在重置密码的时候有没有想过这个问题。回答这个问题其实就一句话:因为服务端也不知道你的原密码是什么。
密码的存储方式
我们注册用户之后用户信息会被存储到服务端的数据库中,但是这个密码不是直接以明文的方式存储的,如果以明文的方式存储那就是严重的安全风险问题了。密码基本都是通过哈希算法来加密密码并保存的,例如经常使用的 MD5 加密方式。
当我们注册用户时输入明文密码之后上传到服务器,服务器会使用加密算法将你的明文密码进行加密再存储到数据库中。而且这个加密一般是不可逆的,即只能通过明文加密到密文,但是不能将密文解密为明文。等用户登录的时候会再次将用户输入的明文密码进行加密与数据库中的密码进行比对。当我们忘记密码的时候服务端也不知道你的密码是什么,所以只能让用户重置密码,而不是直接告诉用户密码。
MD5加密的缺陷
MD5 理论上是不可逆的,所以从理论上来说这个加密后的代码是不可解析的。但是 MD5 有个比较严重的问题就是:同样的字符串加密之后会得到同样的结果。
这也就意味着:E10ADC3949BA59ABBE56E057F20F883E 代表的永远都会是 123456。所以,如果有一个很大的 md5 密码库,那么理论上就可以解析出所有的 md5 加密后的字符串。如下图一样,我们可以拿到MD5加密后的字符串进行“解密”:
如何使密码更安全
为了增加破解难度,通常可以选择加盐。盐(Salt)在密码学中,是指通过在密码任意固定位置插入特定的字符串,让哈希后的结果和使用原始密码的哈希结果不相符,这种过程称之为“加盐”。例如 MD5 + 加盐。这里使用Spring框架提供DigestUtils工具类对123456进行加盐操作并去上面的网站去实验一下看看能不能“解密”。代码如下:
import org.junit.Test;
import org.springframework.util.DigestUtils;
public class Md5Test {
@Test
public void md5Test() {
String password = "123456";
String salt = "BA59ABBE56";
System.out.println(DigestUtils.md5DigestAsHex((password + salt).getBytes()));
// 加密之后的结果为:012df1a669b598d67ec70e761d47c8fd
}
}
但是要注意加盐只是增加了破解密码的难度,并不代表密码无法被破解。想要密码更安全可以使用安全性更高的加密算法,例如 密钥派生算法(Key Derivation Function,简称 KDF,也称为密码哈希算法)。相比其他加密哈希算法,KDF 具有一个独特属性——计算速度很慢,而且从设计上就使其计算速度难以提升,所以 KDF 也被称为 慢哈希算法 。
常见的KDF算法
常见的 KDF 算法主要有(安全程度依次递增):
PBKDF2:其核心是对 HMAC 进行多次迭代以增加破解难度。Bcrypt 对内存的要求较低,并不能抵抗密码破解硬件(如 GPU、ASIC、FPGA)攻击。这个 KDF 算法比较老了,目前已经不推荐使用。
Bcrypt:一种基于 Blowfish 加密算法的密码哈希算法,专门为密码加密而设计,安全性高于 PBKDF2。Bcrypt 对内存的要求较低,同样不能抵抗密码破解硬件攻击。
Scrypt:相比于 PBKDF2 和 Bcrypt,其占用的内存更多,安全性也要更高。它还可以通过调整内存和 CPU 的使用量来增加破解的难度。Argon2:目前最强的密码 Hash 算法,在 2015 年赢得了密码 Hash 竞赛。和 Scrypt 一样,Argon2 同样需要大量的内存。二者综合使用加盐、多次迭代、大量消耗 CPU 时间和内存资源等手段,大大提升了对抗密码破解硬件的能力。
Spring Security 提供了这些 KDF 算法的实现。
参考资料:
- 面试官:为什么忘记密码要重置而不是告诉你原密码?
- 求你了,别再用 MD5 加密了!