如何实现“忘记密码”功能?

信息安全 密码管理 重设密码
2021-09-05 05:29:00

对于我的项目,我需要一个“忘记密码”功能。

我不太确定如何实现这种功能,所以我希望在互联网上找到一些“最佳实践”,但找不到任何有用的东西来处理这个非常常见的功能的每个重要方面。


我自己对此的想法是相当直截了当的:

如果用户想更改他的密码,我可以创建一个uniqueTokenForTheUser附加到“更改密码请求”的唯一令牌,这基本上是我的后端的标识符,以判断是否是发送请求的正确用户。

因此,例如,我会生成/发送一封user@example.com带有链接的电子邮件

http://www.example.com/changePassword?token=uniqueTokenForTheUser

uniqueTokenForTheUser 将存储在一个表中,该表在某个时间间隔内由线程检查,并在令牌过期后删除记录,以防用户实际上没有更改他的密码。

虽然这对我来说听起来很简单,但我想仔细检查是否有任何“最佳实践”,例如在生成和删除该特定令牌时。

欢迎任何对文章/教程的建议或参考!


评论建议的其他链接:

4个回答

仅为此使用 HTTPS,然后我们将了解实现的细节。

首先,您要防止用户枚举

也就是说,不要在页面中显示用户是否存在。这应该只在电子邮件本身中披露。

其次,您将要避免引荐来源网址泄漏确保目标链接上不存在外部链接或外部资源。缓解这种情况的一种方法是在遵循初始链接之后进行重定向,以便令牌不再位于查询字符串中。请注意,如果出现问题(例如,您的数据库暂时关闭),并且显示标准错误模板,那么如果令牌包含任何外部引用,则可能会导致令牌泄漏。

使用 CSPRNG 生成具有 128 位熵的令牌。使用 SHA-2 存储此服务器端,以防止重置表上的任何数据泄漏漏洞允许攻击者重置密码。请注意,不需要盐。在短时间内(例如几个小时)使这些令牌过期。

除了在电子邮件链接中向用户提供非散列令牌外,还可以为用户提供手动导航到页面然后粘贴令牌值的选项。这可以防止查询字符串值被记录在浏览器历史记录中,默认情况下也可以记录在任何代理和服务器日志中。

或者,您可能希望确保启动密码重置的会话的会话标识符是链接后面的那个这可以帮助保护攻击者可以访问用户电子邮件的帐户(例如,设置对电子邮件帐户的访问受限的快速转发所有规则)。然而,这实际上是为了防止攻击者伺机跟随用户请求重置——如果攻击者想要针对您的特定站点,攻击者仍然可以为受害者请求他们自己的重置令牌。

一旦密码被重置为用户的选择之一,您将需要使令牌过期并向用户发送一封电子邮件,让他们知道这已经发生,以防攻击者以某种方式设法重置他们的密码(不一定有控制权)他们的邮件帐户) - 深度防御。还应该轮换当前会话标识符并使​​该用户帐户的任何其他标识符过期. 这消除了用户必须再次登录的需要,同时还清除了攻击者所使用的任何会话,尽管一些用户喜欢让网站将它们注销,这样他们就可以放心地确认他们的密码实际上已被重置。重定向到登录页面也使一些密码管理员有机会保存登录 URL 和新密码,尽管许多人已经从重置页面检测到新密码。

您可能还希望为重置机制和更改通知考虑其他带外选项。例如,通过短信或手机提醒。

你的思路是正确的。但是,我建议从前一步描述您忘记密码功能的流程。有人声称忘记了他们的密码,您需要确保您确定此人确实是您将启动密码恢复程序的帐户的所有者。

下面的流程假设人们可以在其中注册自己的服务,例如网站(换句话说,可以使用注册功能构建已注册电子邮件和/或用户名的列表)。

第一步:识别用户

  1. 单击“糟糕,我忘记了密码”链接后,向用户显示一个表单,他们在其中输入用户名和电子邮件地址(可选地以以下形式向他们提示电子邮件地址:'s.....@ o....com');
  2. 通知他们输入的电子邮件地址是否属于他们输入的用户名(是或否);
  3. 如果输入的用户名和密码匹配,请继续下一步。

第二步:开始恢复程序

  1. 生成一个随机恢复令牌(128 位应该没问题);
  2. 在您的数据库中存储此令牌的哈希(使用密码哈希算法创建)及其创建时间戳;
  3. 向用户发送一封电子邮件,其中包含指向更改密码表单的链接(包含恢复令牌)。更改密码表单的 URL 应为 HTTPS。
  4. 当您收到更改密码请求时,请检查:[1]。令牌的有效性(使用它的创建时间与当前时间相比,通常 10 分钟的时间窗口就足够了);[2]。如果提供的令牌是正确的(对令牌进行哈希处理,将生成的哈希与数据库中的哈希进行比较,就像输入密码一样);
  5. 如果所有检查都通过,则继续下一步。

第三步:更新密码

  1. 销毁您可能为此用户存储的所有“记住我”信息;
  2. 销毁与该用户关联的所有活动会话;
  3. 更新用户密码;
  4. 可选地向他们的电子邮件地址发送电子邮件,通知他们更改;
  5. 登录用户。

您的方法接近最佳实践。一个小的补充是:

  • 您可能要求用户名与令牌匹配,而不是仅仅从令牌中扣除用户。

  • 您应该使用 https 而不是 http。

其他问题:

  • 至于生成:生成它们的常用方法是使用 UUID,但正如评论指出的那样,您应该使用完全随机的、足够长的字符串。

  • 至于删除:您可以按照您的建议定期清理旧的、未使用的令牌。也许使用允许邮件通过通常的电子邮件灰名单的时间范围,例如 15 分钟。

对此的一些想法:

  • 电子邮件本质上是不安全的。攻击者可能会在您的服务器和用户收件箱之间的任何地方窃取令牌。如果可能,请考虑使用另一种通信媒介,例如 SMS。
  • 如果您通过电子邮件访问,则恢复链接应该通过 HTTPS,而不是 HTTP。至少该过程的那部分可以被加密。
  • 为了防止用户枚举攻击,您可能希望显示相同的消息,或者用户输入的用户名/电子邮件地址是否存在。(至少不要在用户只输入用户名后错误地显示电子邮件。)
  • 使用好的伪随机数生成器和长标记。没有人应该能够猜到令牌。为了使令牌猜测更加困难,要求用户同时输入用户名和令牌。限制尝试次数(每个用户名和/或 IP)也将防止暴力破解。也许也是验证码?
  • 您想限制令牌的有效期。如果电子邮件卡在过滤器中,它可能会很慢,但我看不出任何人需要超过半小时的理由。
  • 恢复帐户后,结束该帐户的所有旧会话。用户可能希望恢复帐户,因为它已被入侵,然后攻击者应该被注销。
  • 只发送一个使用令牌,而不是新密码!永远不要通过电子邮件发送密码。
  • 只允许使用一次令牌!使用后立即删除(即使用户实际上并未更改密码)。

有关该主题的更多阅读,我推荐Troy Hunt