ChatGPT解决这个技术问题 Extra ChatGPT

使用 JWT 令牌认证时是否真的需要刷新令牌?

我正在引用另一篇讨论使用 JWT 刷新令牌的 SO 帖子。

JWT (JSON Web Token) automatic prolongation of expiration

我有一个具有非常通用架构的应用程序,我的客户端(Web 和移动设备)与 REST API 通信,然后再与服务层和数据层通信。

https://i.stack.imgur.com/4estc.png

我了解 JWT 令牌身份验证,但我对如何使用刷新令牌有点困惑。

我希望我的 JWT 身份验证具有以下属性:

JWT Token 的有效期为 2 小时。客户端每小时刷新一次令牌。如果用户令牌未刷新(用户处于非活动状态且应用程序未打开)并过期,则他们需要在想要恢复时登录。

我看到很多人声称使用刷新令牌的概念可以使这成为更好的体验,但是,我看不到这样做的好处。管理它似乎增加了复杂性。

我的问题如下:

如果我要使用刷新令牌,那么为该令牌的良好实践长期到期是否仍然有益?如果我要使用刷新令牌,该令牌会与 userId 和/或 JWT 令牌一起保存吗?当我每 1 小时更新一次令牌时,这是如何工作的?我是否要创建一个接收 JWT 令牌或刷新令牌的端点?这会更新我的原始 JWT 令牌的到期日期,还是创建一个新令牌?鉴于这些详细信息,是否需要刷新令牌?似乎如果用户只是使用 JWT 令牌来获取新令牌(根据上面的链接),那么刷新令牌已过时。


C
Charlie Flowers

稍后让我来回答您的问题,并从实际讨论刷新令牌的全部目的开始。

所以情况是:

用户打开应用程序并提供他的登录凭据。现在,该应用程序很可能正在与 REST 后端服务进行交互。 REST 是无状态的,无法授权访问 API。因此,到目前为止,在讨论中,无法检查授权用户是否正在访问 API,或者只是一些随机请求通过。

现在为了能够解决这个问题,我们需要一种方法来知道请求来自授权用户。所以,我们所做的是引入一种叫做访问令牌的东西。所以现在一旦用户成功通过身份验证,他就会获得一个访问令牌。这个令牌应该是一个长且高度随机的令牌(以确保它不会被猜到)。这就是 JWT 发挥作用的地方。现在您可能/可能不想在 JWT 令牌中存储任何用户特定的详细信息。理想情况下,您只想在 JWT 中存储非常简单、极其不敏感的细节。 JWT(正在使用的库)本身负责操作 JWT 哈希以检索其他用户的详细信息(IDOR 等)。

所以,现在,我们的授权访问问题已经解决了。

现在我们讨论一个攻击场景。假设使用上述所有用户 Alice,使用该应用程序,拥有授权访问令牌,现在她的应用程序可以向所有 API 发出请求并根据她的授权检索数据。

假设 Alice 以某种方式丢失了访问令牌,或者换句话说,对手 Bob 获得了对 Alice 的访问令牌的访问权。现在,尽管 Bob 未经授权,但可以向 Alice 授权的所有 API 发出请求。

我们理想中不想要的东西。

现在这个问题的解决方案是:

要么检测到有这种事情发生。减少攻击窗口本身。

仅使用访问令牌,很难实现上述条件 1,因为无论是 Alice 还是 Bob,都使用相同的授权令牌,因此来自两个用户的请求是不可区分的。

因此,我们尝试实现上述 2,因此我们为访问令牌的有效性添加了一个过期时间,例如访问令牌在“t”(短期)时间内有效。

它有什么帮助?好吧,即使 Bob 拥有访问令牌,他也只能在它有效时使用它。一旦过期,他将不得不再次取回它。现在,当然,你可以说他可以像第一次一样得到它。但话又说回来,没有什么比 100% 安全!

上述方法仍然存在问题,并且在某些情况下是不可接受的。当访问令牌过期时,它将要求用户输入他的登录凭据并再次获得授权的访问令牌,至少在移动应用程序的情况下,这是一种糟糕的(不可接受的)用户体验。

解决方案:这就是刷新令牌的用武之地。它也是一个随机的不可预测的令牌,它也首先与访问令牌一起发布给应用程序。这个刷新令牌是一个非常长寿命的特殊令牌,它确保一旦访问令牌过期,它就会向服务器请求新的访问令牌,从而无需用户重新输入他的登录凭据来检索一个新的授权访问令牌,一旦现有的已过期。

现在您可能会问,Bob 也可以访问刷新令牌,类似于他破坏访问令牌的方式。是的。他可以。然而,现在很容易识别这种事件,这在仅使用访问令牌的情况下是不可能的,并采取必要的措施来减少造成的损害。

如何?

对于每个经过身份验证的用户(通常在移动应用程序的情况下),都会向应用程序颁发一对一映射的刷新令牌和访问令牌对。因此,在任何给定时间点,对于单个经过身份验证的用户,只有一个访问令牌对应于刷新令牌。现在假设如果 Bob 泄露了刷新令牌,他将使用它来生成访问令牌(因为访问令牌是唯一被授权通过 API 访问资源的东西)。一旦 Bob(攻击者)请求使用新生成的访问令牌,因为 Alice(真正的用户)的访问令牌仍然有效,服务器就会将此视为异常,因为对于单个刷新令牌,在一次。识别异常,服务器将销毁有问题的刷新令牌,并且连同它相关的访问令牌也将失效。从而防止对任何需要资源的授权的任何进一步访问,无论是真实的还是恶意的。用户 Alice 将被要求再次使用她的凭据进行身份验证并获取一对有效的刷新和访问令牌。

当然,您仍然可以争辩说 Bob 可以再次获得对刷新和访问令牌的访问权并重复上面的整个故事,这可能会导致对真正的真正客户 Alice 的 DoS,但是再一次没有什么比 100% 安全.

同样作为一种好的做法,刷新令牌应该有一个到期时间,虽然很长。


这是一个很好的答案,给我带来了一些问题。如果 Bob 无法访问 Alice 的电话并且令牌仅通过 HTTPS 发送,他有什么可能窃取访问令牌的方法?您说“对于每个经过身份验证的用户(通常是移动应用程序),都会向应用程序发出一对一映射的刷新令牌和访问令牌对。”这是否意味着 Alice 不能在她的手机和台式机上使用相同的令牌?如果是这样,它实际上等同于 Bob 在不同的机器上使用相同的令牌,对吧?
@nomad 可以通过多种方式破坏访问令牌。 1. 丢失设备。 2. 应用程序有一些漏洞将令牌泄露给设备上的其他应用程序 3. 底层操作系统版本本身有漏洞,可能是也可能不是零天 4. 用户自己嗅探自己的流量(HTTPS 不会真正有帮助)要获取访问令牌,并且在没有过期的情况下使用令牌,即使在例如,她已被阻止使用该应用程序等之后。对于第二个任务,猜测每个新设备,整个身份验证流程将重复发出授权。开放讨论。
此外,在以下场景中:“一旦 Bob(攻击者)使用新生成的访问令牌发出请求,因为 Alice(真正的用户)的访问令牌仍然有效,服务器会将此视为异常,因为对于单个刷新令牌一次只能有一个授权访问令牌”,服务器如何知道这是异常?因为现有的访问令牌还没有过期?如果是这样,那与到期前的合法刷新调用有何不同?
如果 Alice 有一段时间没有登录来刷新她的访问令牌怎么办?假设爱丽丝在晚上签了名,她的访问令牌自然会过期,她的刷新令牌在几天内仍然有效。 Bob 在这种情况下不能使用 Alice 的刷新令牌来生成新的访问令牌吗?由于它们不是在数据库中与刷新令牌配对的有效访问令牌,因为它会自然过期。我可能理解最后一次检查错误,但这听起来像是查看某人是否获得您的刷新令牌的唯一方法是检查在发出请求时是否只有一个有效的访问令牌。
有一些缺陷的好答案。 “识别异常,服务器将销毁有问题的刷新令牌,随之而来的是,与之相关的访问令牌也将失效”。这不会自动发生。使刷新令牌无效并不意味着访问令牌将被销毁。访问令牌将在到期后失效。 “因为对于单个刷新令牌,一次只能有一个授权访问令牌”人们可以在其到期之前抢先请求更多访问令牌。所以这看起来不正确。
a
alexishevia

我相信在这种情况下,您可以单独使用访问令牌,让您的客户的生活更轻松,同时保持刷新令牌的安全优势。

这就是它的工作方式:

当您的用户使用凭据(用户名/密码)登录时,您会返回一个短暂的 JWT。您还可以在其中存储一个数据库记录: JWT id 用户 id IP 地址 用户代理 一个有效标志(默认为 TRUE) createdAt updatedAt 您的客户端在每个请求中提交 JWT。只要 JWT 没有过期,它就可以访问资源。如果 JWT 过期,您将在后台刷新它并返回资源和带有新 JWT 的附加 X-JWT 标头。当客户端收到带有 X-JWT 标头的响应时,它会丢弃旧的 JWT 并将新的 JWT 用于将来的请求。

刷新 JWT 如何在服务器上工作

使用 JWT id 查找匹配的数据库记录。检查有效标志是否仍然为真,否则拒绝。或者,您可以将请求 IP 地址和用户代理与存储的 IP 地址和用户代理进行比较,如果某些内容看起来可疑,则决定拒绝。或者,您可以检查 db 记录的 createdAt 或 updatedAt 字段,并在时间过长时决定不刷新。更新 db 记录中的 updatedAt 字段。返回新的 JWT(它基本上是过期 JWT 的副本,但具有延长的过期时间)。

这种设计还可以让您选择撤销用户的所有令牌(例如,如果用户丢失了他的手机或更新了他的密码)。

好处:

您的客户端永远不必检查过期时间或发出刷新令牌请求,它所做的只是检查响应中的 X-JWT 标头。

您可以根据 IP 地址、用户代理、最大令牌年龄或这些的组合添加自定义刷新逻辑。

您可以为用户撤销部分或全部令牌。


旁注:如果我们发出 CORS 请求,自定义 X-JWT 标头将不可用。
@tuler 如果要在 CORS 上公开自定义 X-JWT 标头,则需要将其包含在 Access-Control-Expose-Headers 标头中。另一种选择是将其作为元数据包含在响应正文中。
为什么要返回新的 JWT(基本上是过期 JWT 的副本?它的全部意义不在于更改令牌,给用户新的令牌吗?
@alexishevia 为什么要返回新的 JWT (基本上是过期 JWT 的副本?更改令牌的全部意义不在于给用户新的令牌吗?
P
Pang

如果我要使用刷新令牌,那么为该令牌的良好实践长期到期是否仍然有益?

刷新令牌是长期存在的,访问令牌是短暂的。

如果我要使用刷新令牌,该令牌会与 userId 和/或 JWT 令牌一起保存吗?

它将在客户端上作为单独的令牌与 JWT 一起保存,但不在 JWT 内部。 UserID/UID 可以存储在 JWT 令牌本身中。

当我每 1 小时更新一次令牌时,这是如何工作的?我是否要创建一个接收 JWT 令牌或刷新令牌的端点?这会更新我的原始 JWT 令牌的到期日期,还是创建一个新令牌?

是的,您需要一个单独的服务来发布和刷新令牌。它不会更新现有 JWT 令牌的到期时间。令牌只是经过 base64 编码的 JSON 字段值对。所以改变数据,改变输出。该令牌还具有发行日期,至少会在每个新发行(刷新)时更改。因此,每个代币都将是独一无二的和新的。旧令牌将自动过期,因此您需要所有访问令牌都过期,否则它们将永远存在。

此处的另一个答案指出,当您发行新令牌时,旧令牌会被销毁。事实并非如此。令牌不能被销毁。事实上,您可以通过不断联系身份验证服务器并使用刷新令牌请求新的新令牌来获取数百个令牌。这些访问令牌中的每一个都将在其到期之前有效。所以到期是必要的,而且应该很短。

考虑到这些细节,真的需要刷新令牌吗?似乎如果用户只是使用 JWT 令牌来获取新令牌(根据上面的链接),那么刷新令牌已过时。

JWT 令牌具有客户端声明。例如,对 JWT 令牌的 is_manager:true 声明可能允许访问经理级功能。现在,如果您决定将用户从经理降级为承包商,则不会立即生效。用户可能仍在使用旧令牌。最后,当到期时,他点击身份验证服务器以刷新他的令牌。身份验证服务器在没有管理声明的情况下发出新令牌,用户将无法再访问管理功能。这会创建一个窗口,在此期间用户的声明与服务器不同步。这再次解释了为什么访问令牌应该是短暂的,因此同步可以经常发生。

本质上,您每 15 分钟更新一次授权检查,而不是对每个请求进行检查(这是典型的基于会话的身份验证的工作方式)。如果您想要实时权限而不是每 15 分钟刷新一次,那么 JWT may not be a good fit


“令牌不能被销毁。”。谢谢你。我不敢相信另一个答案得到了这么多票。 . . JWT 的全部意义在于您不需要数据库来检查哪些是有效的。它应该是无国籍的。 .
实际上,我会对管理员访问实施双重检查。如果 isManager 为真,它只是意味着检查数据库是否有管理员访问权限。如果标志为假,则用户立即被拒绝该资源。
您可以采用混合方法,在内存中保存一个“未过期但无效”令牌列表(如果您的应用程序有少量用户)。实际的 'not-expired-but-invalid' 令牌保存在 redis 中,并带有过期的 TTL——它们每秒从 redis 后台刷新——这样您还可以为用户提供注销功能。
虽然您确实不需要用于令牌的中央数据库,但您可以通过更新用于创建令牌的密钥来有效地使令牌无效。这是一个好主意,例如当用户更新其凭据或使设备无效时。