使用 jwt 结构重置密码

Posted

技术标签:

【中文标题】使用 jwt 结构重置密码【英文标题】:password reset with jwt structure 【发布时间】:2018-11-27 17:19:25 【问题描述】:

我正在尝试编写身份验证应用程序的密码重置部分。我选择使用 JWT、node.js 和 express,我使用以下逻辑:首先,用户输入他们的电子邮件,然后生成一个令牌并通过密码重置链接发送到用户的邮件。其次,当用户按下链接时,会设置一个功能来检查令牌是否正确以及它是否仍然有效,第三,我有一个功能可以将新密码保存到数据库中。

我不确定的是应该检查令牌的第二步。一些教程说您应该将令牌保存到数据库中,然后将链接中的令牌与数据库中的令牌进行比较。但是,使用 JWT 不将任何内容保存到数据库中作为参考难道不是重点吗?我不应该只使用 jwt.verify 来获取保存在令牌中的信息,然后检查数据库中的用户以及它是否仍然处于活动状态?

这是使用 JWT 的正确方法吗?或者你会推荐我使用 session 而不是 JWT?

【问题讨论】:

创建token的时候可以设置一个过期时间,然后验证时会检查token是否有效,不需要保存到db。但是您需要保存生成和验证令牌所需的秘密。 是的,秘密将被保存在数据库中。但是这样token就不用存到数据库只存到header了? 不,除非您需要添加黑名单以防止此令牌被多次使用,否则您不需要它。否则当你验证它过期或不正确时会出错。客户端发送请求时,需要在header中添加token供后端验证。 Should I store JWT tokens in redis?的可能重复 【参考方案1】:

this answer 有一个很好的建议。您可以使用当前存储的密码值的一些哈希值作为密码重置 JWT 的一部分。

因此有效载荷可能包含 sub: user_id, exp: "now + 10 minutes", purpose: "password_reset", key: hash(hashed_password_from_db).substr(0, 6) 。此令牌只能成功使用一次。

【讨论】:

我希望你没有在你的数据库中存储密码。引用的文章建议使用已经散列的密码(在您的数据库中)作为密钥。 让我修改一下。我的意思是哈希用于签署 JWT。我不确定您使用 key 属性的目的是什么。 我重命名了该字段以使其更清晰。 key 字段的要点是确保每个密码重置令牌只能重置一次密码。在任何成功重置后,令牌应该是无效的。【参考方案2】:

在使用 JWT 实现密码重置时存在一个简单的缺陷。 从您当前的实现中,用户可以多次生成重置密码链接。因此,用户可以在给定时间内拥有许多活动的重置令牌。

是的,可以采用 JWT 无状态,但在这种情况下效率不高,因为即使在用户重置密码后,您也可以拥有多个可用于重置密码的令牌(取决于你的方法)

我在一个测试和安全性至关重要的组织工作。不允许您的实施。 规则是一次只能激活一个重置密码链接。

所以 JWT 令牌对我们来说不是最佳选择

所以我要做的是生成一个保存在数据库中的随机令牌(也与当前时间一起)。该令牌用于识别用户,时间用于验证用户是否在给定时间内重置。

当令牌处于活动状态时,如果用户决定再次生成令牌,则在生成新令牌之前,前一个令牌将处于非活动状态。

这种方法的优点是您一次只能拥有一个活动令牌。

最后,如果您不介意用户一次拥有多个活动令牌/链接,则应使用 JWT。

【讨论】:

一个技巧是使用用户的密码哈希作为 HMAC 密钥。因此,您可以生成尽可能多的重置链接,但只允许一个。一旦密码更改,密码哈希(可能是 BCRYPT)也会更改,这会使其余的令牌无效。我还建议在每次服务器启动时使用带有随机密钥的加密 JWT。

以上是关于使用 jwt 结构重置密码的主要内容,如果未能解决你的问题,请参考以下文章

如何将 jwt 令牌添加到 url?

mysql重置密码,忘记密码,重置root密码,重置mysql密码

vue添加用户密码邮箱重置修改删除v

重置mysql密码

zabbix登陆密码忘记如何重置

mysql忘记密码后重置密码的方法(mysql 8.0.25重置密码)