在node.js中更改密码和注销时使JWT无效的最佳实践?[关闭]


72

我想了解在更改密码/注销时不击db即可使JWT无效的最佳实践。

我下面的想法是通过点击用户数据库来处理上述2种情况。

1.万一密码更改,我检查存储在用户数据库中的密码(散列)。

2.在注销的情况下,我将上次注销时间保存在用户数据库中,因此通过比较令牌创建时间和注销时间,我可以使这种情况无效。

但是,这两种情况都是以用户每次点击api时每次点击用户db为代价的。任何最佳实践都值得赞赏。

更新: 我不认为我们能够在不影响db的情况下使JWT失效。所以我想出了一个解决方案。我已经发布了我的答案,如果您有任何疑问,欢迎您。

Answers:


80

使用无刷新令牌时:

1.更改密码时:用户更改密码时,请在用户数据库中记录更改密码时间,因此,如果更改密码时间大于令牌创建时间,则令牌无效。因此,剩余的会话将很快注销。

2.当用户注销时:用户注销时,将令牌保存在单独的DB中(例如:InvalidTokenDB,并在令牌到期时从Db中删除令牌)。因此,用户从相应的设备注销,他在其他设备中的会话不受干扰。

因此,在使JWT无效的同时,我遵循以下步骤:

  1. 检查令牌是否有效。
  2. 如果有效,请检查它是否存在于invalidTokenDB(一个已注销的令牌存储到其到期时间的数据库)中。
  3. 如果不存在,请在用户数据库中检查令牌创建时间和更改的密码时间。
  4. 如果更改的密码时间<令牌创建时间,则令牌有效。

与上述方法有关

  1. 对于每个api请求,我需要执行上述所有步骤,这可能会影响性能。

当使用刷新令牌时:访问令牌的有效期为1天,刷新令牌作为生存期

1.更改密码时:用户更改密码时,请更改用户的刷新令牌。因此,剩余的会话将很快注销。

2.当用户注销时:当用户注销时,将令牌保存在单独的DB中(例如:InvalidTokenDB,并在令牌到期时从Db中删除令牌)。因此,用户从相应的设备注销,他在其他设备中的会话不受干扰。

因此,在使JWT无效的同时,我遵循以下步骤:

  1. 检查令牌是否有效
  2. 如果有效,请检查令牌是否存在于InvalidTokenDB中。
  3. 如果不存在,请使用userDB中的刷新令牌检查刷新令牌。
  4. 如果等于,则其为有效令牌

与上述方法有关

  1. 对于每个api请求,我需要执行上述所有步骤,这可能会影响性能。
  2. 我如何使刷新令牌无效,因为刷新令牌没有有效性,如果黑客使用它,但身份验证仍然有效,请求将始终是成功的。

注意:尽管Hanz在基于令牌的身份验证使用刷新令牌中建议了一种保护刷新令牌的方法是否安全?,我听不懂他在说什么。任何帮助表示赞赏。

因此,如果有人有很好的建议,欢迎您提出意见。

更新: 我添加了答案,以防您的应用程序不需要刷新令牌且使用寿命满的情况。Sudhanshuhttps://stackoverflow.com/users/4062630/sudhanshu-gaur)给出了这个答案。感谢Sudhanshu。所以我认为这是最好的方法,

当不需要刷新令牌并且访问令牌没有过期时:

用户登录时,请在其用户数据库中创建一个没有过期时间的登录令牌。

因此,在使JWT无效的同时,请执行以下步骤,

  1. 检索用户信息,并检查令牌是否在其用户数据库中。如果可以的话。
  2. 用户注销时,仅从用户数据库中删除此令牌。
  3. 当用户更改密码时,请从用户数据库中删除所有令牌,然后要求他再次登录。

因此,使用这种方法,您无需在数据库过期之前将注销令牌存储在数据库中,也无需在上述情况下更改密码时存储令牌创建时间。但是,我相信这种方法仅在您的应用程序具有不需要刷新令牌且令牌没有到期的要求时才有效。

如果有人对这种方法感到担心,请告诉我。欢迎您发表评论:)


我想出了与您的男人相同的方法,但是您还应该在更改密码字段中添加到期时间,请参见下面的答案:)
Sudhanshu Gaur

1
而不是普通数据库,您可以使用redis,因为它位于内存缓存中,因此查找时间将非常少
Sudhanshu Gaur

如果令牌创建时间早于更改的密码时间,令牌是否应该无效?
el_pup_le '16

55
在数据库/数据存储中查找不会破坏JWT的目的吗?
Metalstorm

14
@Metalstorm:此讨论全部涉及如何在更改密码或用户注销(使用或不使用刷新令牌)时使JWT无效。如果您知道在没有数据库交互的情况下可以处理这种情况的任何方法,欢迎发布它:)
Gopinath Shiva

15

我不知道在没有涉及数据库的任何一种方式的情况下随意使令牌无效的方法。

如果可以在多个设备上访问服务,请小心使用方法2。考虑以下情形...

  • 用户使用iPad登录,令牌1已发行并存储。
  • 用户在网站上登录。令牌2已发行。用户注销。
  • 用户尝试使用iPad,在用户从网站注销之前已发行了令牌1,令牌1现在被视为无效。

您可能想看看刷新令牌的想法,尽管它们也需要数据库存储。

另请参见此处,针对类似问题进行了很好的SO讨论,尤其是IanB的解决方案,它可以节省一些数据库调用。

拟议的解决方案 就我个人而言,这就是我要采取的方式...用户进行身份验证,由访问令牌颁发,该令牌具有较短的到期时间(例如15分钟),并且刷新令牌的有效期较长或无限期。将此刷新令牌的记录存储在数据库中。

每当用户处于“活动状态”时,每次都发出一个新的身份验证令牌(每次有效期为15分钟)。如果用户超过15分钟没有活动,然后发出请求(因此使用了过期的jwt),请检查刷新令牌的有效性。如果有效(包括数据库检查),则发出新的身份验证令牌。

如果用户在设备上或通过网站“注销”,则销毁客户端的两个访问刷新令牌,并重要地取消使用的刷新令牌的有效性。如果用户在任何设备上更改密码,则撤消其所有刷新令牌,迫使他们在访问令牌过期后立即再次登录。这的确留下了“不确定的窗口”,但这是不可避免的,每次都不会达到db。

使用这种方法还为用户提供了撤消对某些特定设备的访问权限的可能性,如许多主要Web应用程序所看到的那样。


感谢您对第二种方法的反馈。IanB的解决方案在更改密码时提供了良好的做法,但是当用户注销时,我仍然无法理解逻辑。正如您所解释的,当用户注销时,他只需要在当前系统中注销,我该如何实现?
Gopinath Shiva 2015年

@gopinathshiva请参阅上面的新建议解决方案。这限制了数据库命中率,但应提供所需的功能。
DevFox

当用户注销时,如何销毁客户端上的所有现有令牌?同样,如果我这样做,它将在所有设备上注销。但是,这些令牌仍处于有效状态。如果黑客使用该令牌,则身份验证仍然是有效的(假设令牌有效期为1周)。这不是我所需要的。我只想在相应设备上注销用户,但令牌也应得到保护
Gopinath Shiva 2015年

我同意您在更改密码时撤销刷新令牌的回答。但是,如果我在用户注销时撤销刷新令牌,则它将在所有设备上注销,并且用户必须再次登录
Gopinath Shiva 2015年

我在下面发布了我的解决方案,对该问题进行了更新,对于建议的答案,我也有各自的关注。欢迎您发表评论。
Gopinath Shiva 2015年

9

我不确定我是否在这里遗漏了一些东西,但是我发现接受的答案比必要的更为复杂。

我看到必须为每个api请求命中db以验证或使令牌无效,但是正如我在此处看到的那样,整个过程可能更简单。

每当创建jwt时,即在登录或更改/重置密码期间,请将具有用户ID的jwt插入表中,并为每个jwt维护一个jti(基本上是uuid编号)。同样的jti也进入jwt有效负载。有效地,jti唯一地标识了jwt。从多个设备或浏览器访问帐户时,用户可以同时具有多个jwt,在这种情况下,jti会区分设备或用户代理。

因此,表模式将是jti |。用户身份。(以及课程的主要重点)

对于每个api,请检查jti是否在表中,这意味着jwt是有效的。

当用户更改或重置密码时,请从数据库中删除该userId的所有jti。创建一个新的jwt并将一个新的jti插入表中。这将使所有其他设备和浏览器的所有会话无效,但更改或重置密码的会话除外。

当用户注销时,删除该用户的特定jti,但不是全部。将只有一个登录,但没有一个注销。因此,当用户注销时,不应从所有设备注销该用户。但是,删除所有jtis也会从所有设备注销。

因此它将是一张表,没有日期比较。如果不使用刷新令牌,情况也将相同。

但是,为了最大程度地减少db干扰和可能的延迟,缓存的使用肯定会有助于减轻处理时间。

注意:如果您不赞成,请提出理由。


使用jwt时,我不想一直检查数据库。在你的情况下我必须。我认为检查令牌是否无效会便宜得多,因为这种情况并不常见。而且您可以使令牌甚至无效(例如5分钟),而不是有效:它必须尽快有效。
sigi

@sigi我不了解您如何决定何时使所有设备上的用户的jwts无效。我确实想到过在3秒钟内重新发行jwt以使其无效,但是我不知道如何知道哪个jwt无效
Amruta-Pani

1
创建JWT时,您将其存储在数据库中(没关系,因为它仅在登录时发生)。JWT的失效日期会被每次检查。除此之外,还要检查它是否在黑名单中(可以是数据库表,也可以是reddis)。当用户更改其密码时,您将查找该用户的所有JWT,并检查所有仍有效的JWT,并将其列入黑名单。优点:此黑名单要小得多,可以轻松保存在内存中。黑名单不同步/落后几分钟也可以。
sigi

1

如果用户正在更改密码,那么您将在该数据库中命中数据库。但是不想访问数据库进行授权吗?

我发现存储每个用户字符串的好处是,散列在一起的全局共享字符串为我们的JWT实现提供了最大的灵活性。在这种特殊情况下,我将存储密码的哈希值以用于全局字符串,并将它们哈希在一起以得到JWT机密。


-1

我完全同意@gopinath的答案,只是想添加一件事,即当所有令牌都过期时,您还应该删除更改密码时间,例如,假设您为每个令牌设置了3天的过期时间,而不是通常保存更改您还可以将数据库中的密码时间设置为3天,因为显然令牌在此之前就已过期,因此无需再次检查每个令牌其有效时间是否更长,然后更改密码时间


1
很酷,感谢您的回答。我有一个查询,对不起,如果不正确。假设如果您不将更改的密码时间存储在数据库中,则使用旧密码创建的令牌登录也将正确。例如,您已经使用移动设备登录,现在在计算机中更改了密码,但是会话仍在移动设备中运行了三天。我认为在这种情况下,会话不应该在移动设备上运行。仅由于这种情况,我添加了将更改的密码时间存储在数据库中的逻辑。
Gopinath Shiva 2016年

1
我得到了你的回答,但是我告诉你的问题是不同的。您提到模块将处理过期的令牌。我同意应该这样做。但是在这种情况下,可以说我已经使用我的MOBILE密码(旧密码)登录了Jan 1月的应用程序,现在我在PC上更改了Jan 14的应用程序密码。因此,到目前为止,使用我的旧密码生成的所有以前的令牌均不起作用。
Gopinath Shiva

1
现在,如果我没有在数据库中存储,更改密码时间,则无法注销使用旧密码生成的令牌。假设在上面的示例中,在1月13日生成的令牌仍然可以在接下来的3天(即直到1月16日,如果令牌到期设置为3天)内工作。你现在有我吗?
Gopinath Shiva

1
实际上你不明白,我想问的是,就像在移动应用程序中一样,我必须将生存时间设置为生命周期,所以现在我必须将所有注销令牌存储在我的数据库中以保证生命周期正常,因此我认为我应该存储所有登录信息我的数据库中的令牌,当请求到来时,我将检查该令牌是否在数据库的该用户列内(实际上,我的意思是不是将所有注销令牌保存一生(因为它们的数量将变得巨大),为什么不只存储当前登录用户的令牌,一旦他注销,然后将其从我的数据库中删除)
Sudhanshu Gaur

1
用户更改密码时您会错过的一件事是,我将删除所有登录令牌,但已更改的登录令牌已从我的数据库中删除,因此您的第一个查询已经解决,现在我猜第二个查询是的。在将它们到期时将其删除,但是由于到期时间是一生,因此它们不会被删除,男人,您不认为它们会庞大吗?请告诉我是否缺少任何东西?
Sudhanshu Gaur
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.