关于安全性:时间限制,或一次性使用,密码重置令牌?

Time limited, or one time use, password reset tokens?

用户忘记了密码,而且(几乎)所有会员网站都需要一种帮助用户重新登录的方法。

我想实现常见的场景:

  • 用户点击网站,尝试登录,不能,并意识到他们忘记了密码-废话!
  • 用户输入电子邮件地址并单击"忘记密码"
  • 用户收到带有密码重置链接的电子邮件
  • 下面是我计划如何实现这一点(c/asp.net mvc):

  • 当用户输入电子邮件并点击"忘记密码"按钮时,我的网站将生成一个guid,并将其存储在数据库中的成员实体(member.ResetToken中),并将该guid的链接通过电子邮件发送给他们(发送的电子邮件将通知他们只能使用此链接一次)。
  • 用户点击链接,我的网站会根据URL中的member.ResetToken查找他们的帐户。如果找到他们的帐户,向他们显示密码重置表,当他们完成重置后,将从他们的帐户中清除member.ResetToken
  • 我的问题是:保持这样(他们可以在任何时候、现在或将来使用该链接重置密码)或添加时间戳来限制他们必须重置密码的时间?

    从用户体验的角度来看,随时准备好重置密码的能力是很好的,但是我要确保我不会忽略这可能会引起的一些安全问题。


    你的计划实际上是可行的,但也有一些地方需要改进。但首先是你最初关于时限的问题:

    让我们问一个相反的问题:为什么令牌应该保持有效的无穷大?

    没有好处,当两年后可以点击重置链接时,要么用户在大约一小时内点击链接,要么他可能已经忘记了链接(如果需要,还可以请求一个新的链接)。另一方面,能够阅读电子邮件并不一定意味着攻击者必须侵入电子邮件帐户,例如办公室中打开的电子邮件客户端、丢失的手机、USB驱动器上的备份…

    最重要的改进是,您应该只在数据库中存储令牌的散列值。有权访问数据库(SQL注入)的人可以要求为他喜欢的任何电子邮件地址重置密码,因为他可以看到新的令牌,所以他可以使用它来设置自己的密码。

    然后我将这些重置信息存储在一个单独的表中。在那里,您可以存储用户ID、哈希标记、到期日期以及链接是否已被使用的信息。用户当时不处于特殊状态。

    也许我误解了这一点,但是重置链接应该指向一个特殊的页面进行密码重置。当用户进入登录页面时,不应进行特殊处理,登录页面不应知道有一个挂起的密码重置。

    重置令牌应该是不可预测的,这可以通过从操作系统的随机源读取真正的随机代码来实现。


    所以,我在评论中试图回避的这种方法存在一些问题。当您将"确认令牌"存储在用户密码中时,您基本上已经销毁了他们的密码。

    我,一个恶意的人,然后可以采取一个庞大的电子邮件地址列表和一个机器人网络,洪水与密码重置请求您的服务器和锁定您的用户从他们的帐户。当然,您的用户会收到一封电子邮件进行重置,但是如果我能够足够快地重置密码,可能会有电子邮件积压(或者,如果您同步执行,我可能会完成整个应用程序)。

    我,你系统的一个普通用户,可能会尝试重置我的密码,但无法找出为什么我没有收到重置电子邮件,因为我甚至不知道一个垃圾邮件文件夹(或它从未到达)。幸运的是,我只记得密码是什么,但它不再工作,因为密码现在是一个不透明的guid,我基本上死在水里,直到我能找到重置电子邮件。

    这是您应该使用的过程。

  • 生成一个密码重置请求,您可以使用一个guid查找该请求,您也可以通过使用一些私有数据散列该值并在URL中传递该值来保护该请求,以避免快速攻击。您也可以通过使此请求仅在一定时间内有效来锁定它。
  • 一旦有人使用有效的令牌和您指定的任何其他参数跟踪该链接,他们就可以更改密码,此时您可以安全地更改用户密码。
  • 将密码请求标记为已完成,或将其删除。如果您真的担心谁更改了密码,那么您还可以跟踪诸如IP地址之类的信息。
  • 向用户发送一封电子邮件,确认他们更改了密码。
  • 另外,为了防止这种情况已经发生,请确保您正在对用户密码进行哈希和加盐。当你用一个guid替换密码的时候,听起来不像是在做这个,所以你只需要重新检查一下。


    我会提出以下建议:

  • < >在允许用户单击忘记密码按钮之前,需要一些信息。例如,需要电子邮件地址和出生日期。

    理想情况下,您的用户界面不应提供任何反馈,以允许黑客确定其重置请求是否成功。你不希望他们在你的页面上搜索电子邮件地址或出生日期。然而,这是一个可用性权衡,因此,是否这样做取决于您真正需要多少安全性。

    您可能还考虑需要一个验证码,这使得暴力和应用程序DoS攻击更加困难。

  • 尽快使一次性令牌过期。我认为几个小时就够了。你不应该认为电子邮件是私有的——它不是私有的,除非你在基本协议的基础上使用了安全的电子邮件技术(如PGP)(大多数人不这么认为)。你最不想看到的是黑市开放,在那里你的吉他被买卖,这正是如果它们有无限的寿命可能发生的事情。

  • 不要使用guid。它们不是密码随机的,是可以猜测的。我建议您使用加密随机数生成器并将其转换为base64。


  • 用户还忘记重置密码(事情发生了)。出于对密码的偏执,我建议将链接寿命限制为24小时。这应该足够了。它不能解决恶意拦截的问题,但总比什么都没有好。