在没有数据库表的情况下通过电子邮件重置密码

Posted

技术标签:

【中文标题】在没有数据库表的情况下通过电子邮件重置密码【英文标题】:Password reset by email without a database table 【发布时间】:2011-02-14 21:21:59 【问题描述】:

通过邮件重置用户密码的正常流程是这样的:

    生成随机字符串并将其存储在数据库表中 给用户的电子邮件字符串 用户点击包含字符串的链接 已针对数据库验证字符串;如果匹配,则重置用户的密码

但是,维护表格和过期旧字符串等似乎有点不必要的麻烦。这种替代方法有什么明显的缺陷吗?

    生成用户现有密码的 MD5 哈希 向用户发送电子邮件哈希字符串 用户点击包含字符串的链接 通过再次散列现有密码验证字符串;如果匹配,则重置用户的密码

请注意,用户的密码已经以散列和加盐形式存储,我只是再次对其进行散列以获得唯一但可重复的字符串。

是的,有一个明显的“缺陷”:这样生成的重置链接在用户更改密码(点击链接)之前不会过期。不过,我真的不明白为什么这会是一个问题——如果邮箱被盗,用户无论如何都会被搞砸。并且不存在重复使用的风险,因为一旦用户密码更改,重置链接将不再匹配。

【问题讨论】:

【参考方案1】:

要修复 obvious flaw,请将当前日期(以及更多与时间相关的信息表示一天中的当前部分,如果一天太长的话)添加到您正在散列的内容中以生成神秘字符串并检查它 - - 这使字符串“过期”(如果您想要更长的“过期”,您可以检查前一个以及当前日期或分数)。所以在我看来你的方案是相当可行的。

【讨论】:

啊,你打败了我;)。只需确保根据用户时区使用数据:) 如何从用户提交的哈希中推导出到期日期以检查当前日期是否已过?它必须被加密和附加。否则:A)您只是对其进行编码,用户会破坏编码,甚至无法进行时间检查。 B)您将其保留为纯文本,同样的事情。再说一次,如果您有一些全局机密 S 以避免空间浪费,您可以发送用户 HASH(S || time || userpass)time;用户发回hashtime,这将允许服务器根据用户发回的哈希来测试HASH(S || time_user_sent_back || userpass) @Longpoke,如果(例如)字符串应该在2010/05/062010/05/07 上有效,则将2010/05/06 附加到您正在散列的字符串上——就是这样。在检查时,您通过附加今天和昨天的 ISO 格式来检查您获得的哈希值——就是这样,几乎没有火箭科学。如果您想要比一天更好的粒度来实现特殊字符串过期目的,那么您还可以使用适当的 一天的分数 - 例如如果您希望字符串持续约 1.5 天,请在您正在散列的内容中使用“半天”(例如 AM 或 PM)(并检查 3 种可能性)——也不难! 哈哈 没想到,我的方法是笼统的! :)【参考方案2】:

如果有人使用密码哈希访问您的数据库,他们将不知道实际密码。如果您实施此系统,那么他们可以生成重置链接并自行重置密码。使用随机字符串和泄露信息,您可以使所有随机字符串无效,并且即使不知道访问权限,也只有正在重置密码的用户才会受到威胁。不太可能出现这种情况,但考虑到随机字符串的名义开销,可能值得考虑。

【讨论】:

一旦您的网站遭到入侵,您还想重置所有密码,因为攻击者可能在将它们提交到服务器时以明文形式记录了它们(这是不可避免的情况;只有 SSL 客户端证书可以阻止这个)。更不用说攻击者可能在几个小时内破解了 90% 的散列密码。另外,如果您使用 Alex Martelli 的建议,您可以针对妥协时间添加对重置请求的额外检查,这实际上与您刚才的建议相同 :) 除了不是每个用户都会有一个随机字符串,但每个用户实际上都有一个生成的字符串,所以如果你没有立即意识到妥协,那么无论如何只有少数用户会受到影响。当然,除了 90% 的密码较弱。 啊!这绝对是一个真正的漏洞,尽管正如 Longpoke 所说,一旦攻击者进入数据库,所有的赌注都没有了:他们不需要使用重置链接,他们可以直接在用户表中更改密码。 +1 找到它! 我在想象数据库的只读快照,而不是完全的写访问权限。【参考方案3】:

实际上在再次考虑这一点之后,您的方法可能没有比“正常流程”安全。

如果你只是发回HASH(HASH(user's original password)),我可以看到这可以给攻击者利用的场景:

场景 1:

    Jim 在您的网站上注册为 jimjones@microsoft.comJim 请求重置密码,但不使用它。重置电子邮件将永远留在他的收件箱中。 Jim 在您的网站上更改了他的电子邮件地址。 jimjones@gmicrosoft.comBob 入侵。 Bob 现在通过他的分布式 GPGPU 农场进行暴力攻击并恢复 Jim 的密码。

场景 2:

    Jim 为他的银行账户使用密码 jimjonesupinthisma!Jim 在您的站点上注册为 jimjones@microsoft.comjimjones@microsoft.comJims 的银行账户有任何关联。 jimjones@gmicrosoft.comBob 入侵。 Bob 现在请求重置,他现在有 HASH(HASH(jim's password))Bob 现在通过他的分布式 GPGPU 农场进行暴力攻击并恢复 Jim 的密码,然后他用该密码访问 Jims 银行帐户。

场景 3:

(您的网站使用 TLS,用户通过 TLS 注册。)

    Jim 在您的网站上注册为 jimjones@microsoft.comBob 请求重置 Jims 帐户的密码。 Bob 在 Room 641A 为 NSA 工作。 Bob 使用他的全球互联网嗅探器获取HASH(HASH(jim's password)),因为它以明文形式通过电子邮件发送到jimjones@microsoft.comBob 现在通过他的分布式 GPGPU 农场进行暴力攻击并恢复 Jim 的密码。

场景 1 和 2 的变体一直发生(取决于哈希和密码的强度),我不太确定 3。关键是,您的方法会泄露不必要的信息,这确实可以利用攻击者反对你的用户。

我建议您使用与用户密码无关的随机生成的令牌。

【讨论】:

对不起,我在这里不同意:你所有的场景似乎都假设两次加盐和散列的字符串在恢复原始字符串方面有任何用处,但这似乎不是案子。众所周知,MD5 容易受到碰撞攻击,但您假设的黑客需要成功的原像攻击才能从哈希中取回密码。迄今为止已知的针对单轮无盐 MD5 的最佳此类攻击具有 2^123 的复杂度,而破​​解即使是在科幻小说领域也是如此。 您不需要(通常也不希望)发生冲突,只需要一个密码(通过字典,然后通过 1-n 位回退到字符集的暴力破解)。大多数用户的密码都很弱,没有哈希值真的能拯救他们。这些攻击是真实的,尽管不像您听说的典型脚本小子攻击那样普遍。实际上,这更多的是与决心和在正确的时间出现在正确的地点有关。 这些攻击是针对未加盐的 MD5 的,您可以在其中使用彩虹表来反转散列,但这里的密码是加盐和双重散列的。 什么?更强的散列只会减慢它的速度。 Preimage 可能会被盐打败,是的,但是如果不降低服务器的性能,你就不能真正减少蛮力时间。如果您对自己的哈希非常强大如此自信,为什么不发布用户列表及其对应的哈希?【参考方案4】:

假设在一个非常罕见的情况下,您的两个用户即使在将随机盐连接到它之后也具有相同的哈希密码;会有问题吧?我想如果您将电子邮件或 user_id 的 Hashid 添加到重置密码链接中,这不会有什么坏处。

【讨论】:

【参考方案5】:

刚刚路过这个issue,有个想法:

1) 发布一个包含用户帐户信息的 JWT(Json Web 令牌)。令牌有一个过期时间,比如 1 小时。

2) 通过电子邮件/链接将令牌发送给用户

3) 用户点击链接,令牌被发送到服务器端点,令牌被验证。如果有效,则解压缩令牌并更新用户帐户

这种方法有什么缺陷吗?不触及数据库(必要时新用户密码除外)

【讨论】:

是的,您可以多次使用令牌,但我*可以接受。那是一件好事。我将令牌到期时间设置为 1 小时,因此不太可能误用恕我直言。我错过了什么?它是如此简单且效果很好,我一定错过了一些东西。 另外,我会将请求的 IP 地址放入令牌中。然后,在验证时,匹配调用者的 ip。然后只能从发起请求的同一设备使用令牌。这只是额外的措施。 IP 欺骗者仍然需要令牌,并且在 1 小时内。

以上是关于在没有数据库表的情况下通过电子邮件重置密码的主要内容,如果未能解决你的问题,请参考以下文章

MVC 密码重置邮件 NO SIMPLEMEMBERSHIP

无需输入电子邮件即可重置密码(ASP.NET 身份)

Laravel 5 重置密码通知不会发送

在没有提示的情况下从 python-fabric 重置 mysql root 密码

通过邮件找回密码

Flask-登录密码重置