【问题标题】:Is a Refresh Token really necessary when using JWT token authentication?使用 JWT 令牌认证时是否真的需要刷新令牌?
【发布时间】:2015-11-10 16:27:26
【问题描述】:

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

JWT (JSON Web Token) automatic prolongation of expiration

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

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

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

  1. JWT Token 的有效期为 2 小时。

  2. 客户端每小时刷新一次令牌。

  3. 如果用户令牌未刷新(用户处于非活动状态且应用未打开)并过期,则他们需要在想要恢复时登录。

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

我的问题如下:

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

【问题讨论】:

    标签: oauth-2.0 jwt http-token-authentication


    【解决方案1】:

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

    所以情况是:

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

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

    所以,目前,我们的授权访问问题已经解决。

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

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

    我们不想要的东西。

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

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

    仅使用访问令牌,很难实现上述条件 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 有一段时间没有登录以刷新她的访问令牌怎么办?假设 Alice 在晚上签了名,她的访问令牌自然过期,她的刷新令牌在几天内仍然有效。 Bob 在这种情况下不能使用 Alice 的刷新令牌来生成新的访问令牌吗?由于它们不是在数据库中与刷新令牌配对的有效访问令牌,因为它会自然过期。我可能理解最后一次检查是错误的,但这听起来像是查看某人是否获得了您的刷新令牌的唯一方法是检查在发出请求时是否只有一个有效的访问令牌。
    • 有一些缺陷的好答案。 “识别异常,服务器将销毁有问题的刷新令牌,并且连同它相关的访问令牌也将失效”。这不会自动发生。使刷新令牌无效并不意味着访问令牌将被销毁。访问令牌将在到期后失效。 “因为对于单个刷新令牌,一次只能有一个授权访问令牌”可以在其到期之前抢先请求更多访问令牌。所以这看起来不正确。
    【解决方案2】:

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

    这就是它的工作原理:

    1. 当您的用户使用凭据(用户名/密码)登录时,您会返回一个 短暂的智威汤逊。您还可以在存储位置创建一个数据库记录:

      • JWT 标识
      • 用户名
      • IP 地址
      • 用户代理
      • valid 标志(默认为 TRUE)
      • createdAt
      • 更新时间
    2. 您的客户端在每个请求中提交 JWT。只要 JWT 没有过期, 它可以访问资源。如果 JWT 过期,则刷新它 在幕后并返回资源和额外的 X-JWT 标头 使用新的 JWT。

    3. 当客户端收到带有X-JWT 标头的响应时,它会丢弃 旧的 JWT 并将新的 JWT 用于未来的请求。

    刷新 JWT 在服务器上的工作原理

    1. 使用 JWT id 查找匹配的 db 记录。
    2. 检查valid标志是否仍然为真,否则拒绝。
    3. (可选)您可以将请求 IP 地址和用户代理与 存储的 IP 地址和用户代理,如果看起来有问题就决定拒绝 鱼腥味。
    4. (可选)您可以检查数据库记录的 createdAt 或 updatedAt 字段,以及 如果时间过长,决定不刷新。
    5. 更新数据库记录中的updatedAt字段。
    6. 返回新的 JWT(它基本上是过期 JWT 的副本,但具有延长的过期时间)。

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

    好处:

    • 您的客户端永远不必检查过期时间或制作刷新令牌 请求,它所做的只是检查响应中的 X-JWT 标头。
    • 您可以添加基于 IP 地址、用户代理、max-token 的自定义刷新逻辑 年龄,或两者的组合。
    • 您可以为用户撤销部分或全部令牌。

    【讨论】:

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

    如果我要使用刷新令牌,那么对于该令牌的良好做法进行长期到期是否仍然有益?

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

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

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

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

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

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

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

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

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

    【讨论】:

    • "令牌不能被销毁。"。谢谢。我不敢相信另一个答案得到了这么多票。 . . JWT 的全部意义在于您不需要数据库来检查哪些是有效的。它应该是无国籍的。 .
    • 实际上,我会为管理员访问实施双重检查。如果 isManager 为真,它只是意味着检查数据库是否有管理员访问权限。如果标志为假,则用户立即被拒绝该资源。
    • 您可以采用混合方法,将“未过期但无效”令牌列表保存在内存中(如果您的应用程序的用户数量较少)。实际的 'not-expired-but-invalid' 令牌保存在 redis 中,并带有过期的 TTL - 它们每秒从 redis 后台刷新 - 这样您还可以为用户提供注销功能。
    猜你喜欢
    • 2020-05-20
    • 2019-09-19
    • 2019-12-29
    • 1970-01-01
    • 2020-10-07
    • 2017-05-27
    • 2021-08-26
    • 1970-01-01
    • 2019-07-01
    相关资源
    最近更新 更多