为什么面试官在忘记密码时选择重置而非告知原密码的原因及其背后的安全考量

这是一个颇具趣味的面试问题,很简单,但在重置密码时,大家是否曾思考过这个问题?简单来说,答案是:因为服务端并不知道你的原密码。如果服务端知道你的原密码,那将会产生严重的安全隐患。

图片

密码重置的必要性

我们来进一步分析这个问题。

从事过开发工作的朋友应该知道,服务端在将密码存储到数据库时,绝对不能以明文形式存储。如果以明文形式保存密码,风险极大。除了数据库可能被攻击的风险外,即便是有数据库访问权限的服务端人员也可能会恶意利用这些信息,带来不可估量的风险。

通常,我们采用哈希算法对密码进行加密并保存(严格来说,哈希算法并不属于加密算法,但可以用于某些加密场景)。

哈希算法,又称为散列函数或摘要算法,旨在对任意长度的数据生成一个固定长度的唯一标识,称为哈希值、散列值或消息摘要(后文统一称为哈希值)。

图片

哈希算法的分类

哈希算法大致可以分为两类:

  1. 加密哈希算法:这种类型的哈希算法安全性较高,能够提供一定的数据完整性保护和防篡改能力,抵御一定的攻击手段。尽管安全性相对较高,但性能较差,适用于对安全性要求严格的场景。例如 SHA2、SHA3、SM3、RIPEMD-160、BLAKE2、SipHash 等等。

  2. 非加密哈希算法:与之相对,这类哈希算法的安全性相对较低,容易受到暴力破解、冲突攻击等攻击方式的影响,但其性能较高,适合对安全性要求不高的业务场景,例如 CRC32、MurMurHash3、SipHash 等等。

此外,还有一些特殊的哈希算法,例如安全性更高的慢哈希算法

目前,常用的方式是通过 MD5 + Salt 来加密密码。盐(Salt)在密码学中的作用是通过在密码的任意固定位置插入特定字符串,从而使哈希的结果与原始密码的哈希结果不相同,这一过程被称为“加盐”。

然而,这种方式如今已不再推荐,因为 MD5 的安全性较低,抗碰撞性差。想了解更多可以参考我写的文章:简历别再写 MD5 加密密码了!。更建议使用安全性较高的加密哈希算法 + Salt(盐)(如 SHA2、SHA3、SM3)或直接使用慢哈希(如 Bcrypt,更推荐这种方式)。

假设我们使用的是SHA-256 + Salt的方式。

以下是一个简单的代码示例:

String password = "123456";  
String salt = "1abd1c";  
// 创建SHA-256摘要对象  
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");  
messageDigest.update((password + salt).getBytes());  
// 计算哈希值  
byte[] result = messageDigest.digest();  
// 将哈希值转换为十六进制字符串  
String hexString = new HexBinaryAdapter().marshal(result);  
System.out.println("Original String: " + password);  
System.out.println("SHA-256 Hash: " + hexString.toLowerCase());  

输出结果为:

Original String: 123456  
SHA-256 Hash: 424026bb6e21ba5cda976caed81d15a3be7b1b2accabb79878758289df98cbec  

在这个示例中,服务端保存的正是密码“123456”加盐哈希之后的数据,即“424026bb6e21ba5cda976caed81d15a3be7b1b2accabb79878758289df98cbec”。

图片

当你输入密码进行登录时,服务端会先提取你密码对应的盐,然后再次执行获取哈希值的过程。如果最终计算出来的哈希值与数据库中存储的哈希值一致,则表明密码正确。否则,密码就是错误的。

哈希算法是不可逆的,你无法通过哈希得到原始值,因此服务端也无法获知你的原密码,自然也无从告知你。