七的博客

忘记密码后为什么只能进行重置密码

设计

忘记密码后为什么只能进行重置密码

一个很有意思的软件设计问题,当用户忘记了自己账户密码的时候,通常的方案都是让你重置密码,怎么就没哪家 APP 会直接把原来的密码告诉你。

目前各家 APP 的做法对于忘记密码后的处理方式基本都是保持一致的,总结起来就是下面几种:

  • 通过绑定的手机接收验证码,然后重置密码。

  • 通过绑定的邮箱接收重置密码的连接,点击链接进去后重置密码。

  • 答对密保问题后,直接给你发送一个随机的密码。

就是没有直接告诉你原密码的方式,这个问题可以从下面几个角度去解释。

1. 密码通常使用加密进行存储

一个安全的系统,它的用户密码必然不能以明文来存储密码,而必须使用密文进行存储。明文存储密码不能防止内部的员工进行传播,再一个如果数据库不小心被拖了,用户的信息就直接全部泄露出去了。

关于明文存储密码这点上,CSDN 应该是最有发言权的一家了。 在 2011 年直接 600万网站用户的明文密码就给人爬出来了,这里需要注意的是 明文密码

11年那会我接触的一些 PHP 的论坛,很多也是明文进行保存密码的,不过这些论坛的人数估计就几千到几万个,远远没有 CSDN 这么多用户量。而且 CSDN 在作为一个技术类的网站,在 2011 年还使用明文进行保存密码,这操作实在是令人看不懂。

2. 密码常用的加密算法

在软件开发中,密码通常会使用哈希算法来进行来进行加密,然后进行存储。哈希算法有几个特点:

  • 从哈希值不能反推出原始明文密码。
  • 密码不同的时候,计算出来的哈希值通常是不一样的。
  • 相同的密码计算出来的哈希值总是一样的。

通过上面几个特点,对密码使用哈希算法进行加密,是比较合适的一种方式。比如使用 MD5 这种哈希算法来加密明文密码 password123 ,进行哈希算法后的值为 adb1534d61a07f72b0f4a8bc741b752d 。把这一串哈希值存到数据库中,当做用户的密码存起来。这样就可以达到到一个加密的作用,理论上只有用户自己才知道正确的明文密码。

即使系统因为某些漏洞导致数据库的数据泄露了,别人也无法拿到原始密码。 不过这样也有一个漏洞,通常的系统的密码都是 8 - 16 位的大小写字母+数字+几个常用的符号,那么所有可能的密码都可以一个个枚举出来。对这些明文密码使用标准的哈希算法进行计算,然后将这些哈希值整理到一个列表中。 将数据库中的密文密码去这个列表中进行匹配,如果匹配上,就说明对应的明文密码被破解出来了。

密文密码 明文密码
1601bcb307b1f12251362a4832a891cf password121
3a912a786df9d85effa5cf27eed0613b password122
adb1534d61a07f72b0f4a8bc741b752d password123
c1bba561ae7639fc2df4c21ac1447b59 password124

比如上面的明文密码在数据库中存储的值为 adb1534d61a07f72b0f4a8bc741b752d , 那么通过这个列表去匹配,就可以反查出明文密码是 password123

上面这种方式一般叫【彩虹表】,彩虹表(Rainbow Table)就是是一种提前计算好的哈希值列表,专门用于逆向破解哈希函数。它通过保存大量可能的明文密码及其对应的哈希值,让攻击者可以从这个列表中快速查找到对应的明文密码。因为不用对每个密码进行实时哈希计算,这样的效率会特别高。

抵御彩虹表攻击的比较常见的方法,就是在计算哈希值前给每个用户的密码增加一个随机生成的字符串,也称为【盐值】。因为有了这个随机的字符串,这样即使两个相同的密码,计算出来的哈希值也会不同。从一定程度上,可以更加好的保护用户的密码不被破解。

当然更好的办法是使用一些更加强的哈希算法,比如 bcrypt

3. 密码加密后的认证流程

  • 当用户创建账户时,系统将用户输入的明文密码通过哈希算法转换成哈希值。
  • 将哈希值存储到数据库中,而不是存储明文密码。
  • 用户登录的时候,将用户输入的密码通过同样的哈希算法转换成哈希值,然后将这个哈希值跟数据库中存储的哈希值进行比较,如果比较是一样的,那么就说明密码正确,用户登录成功。否则登录就失败。

4. 解释问题

回到最开始的问题【忘记密码后为什么只能进行重置密码?】

当你忘记密码的时候,系统并不知道你的原始密码是什么。因为系统数据库中存储的是密文密码,所以只能引导你进行重置密码。