这是另一个“忘记密码页面如何正确完成”的问题。但是,它明确地解决了我最近遇到的两个实现选项。
我知道的常用方法 [1,2] 是基于只能使用一次且有效性有限的随机令牌:
- 用户访问忘记密码页面
- 用户提供电子邮件地址(或类似地址)
- 如果电子邮件与有效的用户帐户相关联
- 该应用程序生成一个随机的、唯一的令牌 t。t 具有足够的熵(比如 128 位)。
- 该应用程序将随机令牌 t 和创建它的时间戳与用户一起存储在数据库中
- 该应用程序会向用户的电子邮件地址发送一封包含密码重置链接的电子邮件。链接看起来像这样:https ://foo.bar/resetPassword?token=t
- 用户点击电子邮件中的链接
- 该应用程序检查
- 如果给定用户的数据库中有令牌 t
- 如果是,则根据存储的时间戳检查它是否仍然有效
- 如果是,则允许用户访问 resetPassword 页面
- 用户设置新密码
- 之后,应用程序使令牌 t 无效
您可以添加更多安全内容,例如带外频道或安全问题。但对我来说,这是我所知道的常见最佳实践。
现在考虑一种不同的方法。我们将失去一次性使用属性。但是,好处是应用程序根本不需要跟踪令牌状态。
我们将使用 HMAC,而不是生成随机令牌,如下所示:
- 令牌 = HMAC_k(电子邮件,时间戳)
k 是应用程序知道足够熵的秘密。通过电子邮件发送给用户的重置链接如下所示:
当用户访问该链接时,应用程序会按如下方式对其进行验证:
- 根据 URL 参数计算 HMAC:token_calculated = HMAC_k(email, timestamp)
- 检查作为 URL 参数提供的令牌和计算的 HMAC 是否匹配
- 如果是,则根据时间戳检查令牌是否仍然有效
- 如果是,则允许用户访问 resetPassword 页面
在我看来,不必维护服务器端状态的属性比单次使用属性更有价值。这假设在服务器端配置了一个合理的、短暂的令牌有效期——比如 20 到 30 分钟。
我真的很想知道其他人对第二种方法的看法。特别是,如果有任何我可能还没有注意到的缺点。
[1] https://www.owasp.org/index.php/Forgot_Password_Cheat_Sheet
[2] http://www.troyhunt.com/2012/05/everything-you-ever-wanted-to-know.html