为什么要在服务器端存储 JWT 刷新令牌?

Why should I store a JWT refresh token server-side?

我读过几篇文章(例如 this and this, and )建议将刷新令牌服务器端存储在数据库或内存中(或内存存储,例如 Redis)。据我了解,这是为了让它们可以被撤销。

是否有充分的理由按照这些文章的建议存储所有令牌,而不是仅在注销时存储列入黑名单的令牌?如果我理解存储令牌的原因,我肯定可以通过在 Redis 中存储一个令牌 ID 来达到相同的效果(只要 TTL 到期,这样 table 就不会变得笨拙)。

这种方法有缺点吗?如果有,缺点是什么(或者相反,存储所有令牌的优点是什么,而不是仅存储已撤销令牌的列表?)


为了详细说明,为什么我认为有一个撤销列表应该没问题,这是我想象的过程:

我是否遗漏了该流程中需要所有标记列表的关键内容?

拥有所有颁发的令牌列表的好处是您可以全面了解谁已经通过身份验证并且当前可以访问系统。然后,您可以根据任何标准(例如令牌的年龄、与令牌用户关联的角色、IP 地址范围)选择从该列表中撤销一些令牌。

如果您只有一个已撤销令牌的列表,则不可能在运行时选择任意标准来撤销有效令牌的子集。换句话说,如果您没有所有已发行令牌的列表,则无法立即在全局范围内强制执行撤销标准,而只能在向资源服务器提供令牌时执行。

我的目标是技术简单化,因为授权服务器 (AS) 应该为您完成繁重的工作。这里有一些端到端的注释,解释了一些棘手的方面并提出了一个简单的方向。

1.令牌 ISSUING

用户进行身份验证(并可选择同意)并生成令牌 'grant'。然后 AS 应该为您存储刷新令牌,在一个可能命名为 'delegations' 的数据库 table 中。通常,存储的 'token' 将是哈希值而不是实际值,并将链接到应用程序 (client_id) 和用户(主题)。发行的令牌可能具有这些生命周期:

  • 刷新令牌:4 小时
  • 访问令牌:30 分钟

2。令牌刷新

OAuth 客户端(例如移动应用程序)将在授予或 'user session' 的生命周期内静默更新访问令牌。这涉及向 AS 发送刷新令牌并取回新的访问令牌。为此,AS 需要存储刷新令牌的哈希值,以便能够验证输入。

3。默认移除行为

当用户注销时,令牌将从客户端应用程序中清除,因此它们消失了。较新的 OAuth 2.1 建议是使用轮换刷新令牌,其中每次访问令牌刷新也会更新刷新令牌并使前一个令牌无效。在我们的示例中,这意味着被盗刷新令牌的生命周期可能会缩短 - 可能只有 30 分钟。

4.管理员手动撤销

如果出于某种原因您想明确拒绝对特定用户和应用程序的访问,管理员可以使用 AS 数据库并发出这样的命令,尽管管理员 UI 可能会提供更多可视化选项。

  • 从 client_id=[value] 和 subject=[value]
  • 的委托中删除

手动撤销是否可以在人员层面进行管理是值得怀疑的,但这是一个很好的能力,例如在安全审查中。

5.注销时撤销刷新令牌

当然,如果需要,客户端可以在注销时撤销自己的刷新令牌,然后再清除令牌。这还应确保同一授权的任何访问令牌都被授权服务器拒绝。

6.访问令牌有效性

访问令牌是最常见的 JWT。当 JWT 仍有 25 分钟的生存时间时,可能会发生撤销或注销。如果攻击者以某种方式拦截了访问令牌(这通常是不可能的),他们可以继续对您的 APIs 使用它 - 在此期间,AS 永远不会看到访问令牌。

7. API 网关设置

在更复杂的设置中,不透明的访问令牌被发布到互联网客户端,然后被发送到一个 API 网关,网关会像 Phantom Token Pattern 中那样对其进行内省。网关还维护访问令牌结果的缓存。

在撤销时,AS 可以提出一个 Custom Event 来通知 API 网关,然后网关可以为用户清除任何缓存的访问令牌。这应该确保拒绝下一个带有已撤销(或注销)用户的访问令牌的请求。

摘要

除非您处理的是非常安全的域,否则我会以简单为目标并遵循以下两个原则:

  • 将令牌存储和撤销留给授权服务器
  • 保持令牌短暂存在,这样撤销就不再是一个问题

我能想到的唯一决定因素如下:

您/您是否需要在任何时候根据某些任意、监管、法律、完整性、安全等标准动态撤销有效刷新令牌的功能?

如果是这样,您至少需要一个包含所有已发行令牌的列表,以及实施标准逻辑所需的任何元数据。

示例:“由于监管,我需要禁止所有欧盟用户”等同于delete from refresh_tokens were user_ip in <... eu logic ...>