【问题标题】:How to use jti claim in a JWT如何在 JWT 中使用 jti 声明
【发布时间】:2015-05-08 14:12:26
【问题描述】:

JWT spec 提到了一个 jti 声明,据称该声明可用作防止重放攻击的随机数:

“jti”(JWT ID)声明为 JWT 提供了唯一标识符。标识符值的分配方式必须确保相同的值被意外分配给不同的数据对象的概率可以忽略不计;如果应用程序使用多个发行者,则必须防止不同发行者产生的值之间的冲突。 “jti”声明可用于防止 JWT 被重放。 “jti”值是区分大小写的字符串。使用此声明是可选的。

我的问题是,我将如何实现这一点?我是否需要存储以前使用的 jtis 并为每个请求发出一个新的 JWT?如果是这样,这不是违背了 JWT 的目的吗?为什么要使用 JWT 而不是仅将随机生成的会话 ID 存储在数据库中?

我的 REST API 有一个 Mongo 数据库,我不反对添加一个 Redis 实例。有比 JWT 更好的身份验证选项吗?我主要只是不想将密码存储在客户端上,这会消除 HTTP 身份验证作为选项,但是,随着我对 JWT 的深入了解,我开始觉得好像自定义令牌实现或不同的标准可能更适合我的需要。是否有任何 node/express 包用于基于令牌的身份验证,支持令牌撤销和轮换令牌?

不胜感激。

【问题讨论】:

  • 您也可以两者兼得。使用 JWT 发送会话 ID 以及其他一些相对静态的数据,例如发布者、受众等。事实上,我们使用了一个连接中间件模块,它使用 JTI 来携带会话 ID,而这又是进入redis 哈希存储他们的实际会话。两者结合的方法允许您将 JWT 中的相对静态数据与 redis 中相对动态的会话数据分开,即使两者都需要与其会话相关联。
  • 我想我会采取类似的方法,使用 mongo 存储有效令牌和 jwt 进行身份验证。当 jwt 过期时,客户端可以使用令牌请求新的 jwt。这种方法有什么陷阱吗?

标签: node.js rest express restful-authentication jwt


【解决方案1】:

确实,存储所有已发布的 JWT ID 会破坏使用 JWT 的无状态特性。但是,JWT ID 的目的是能够撤销之前发布的 JWT。这可以通过黑名单而不是白名单最容易地实现。如果您已包含“exp”声明(您应该),那么您最终可以在自然过期时清理列入黑名单的 JWT。当然,您可以同时实现其他撤销选项(例如,基于“iat”和“aud”的组合撤销一个客户端的所有令牌)。

【讨论】:

  • 黑名单的一个缺点是,在一个可扩展的系统中,在黑名单的内容还没有完全复制到集群中的所有其他节点之前,用户可能仍然能够在这些节点上执行非法操作.使用白名单更加明确,应该使用。
  • @d4nyll 白名单的一个缺点是,在可扩展的系统中,在对白名单的更新完全复制到集群中的所有其他节点之前,用户可能仍然能够对那些节点。使用白名单不会改变这个问题。
  • @gordonmleigh 否。白名单的想法是只有白名单上的用户才能执行其预期的操作。因此,在将该列表复制到其他节点之前,它们将无法执行其预期的操作。这可能会很痛苦,因为延迟时间更长,但它使系统更加安全。
  • @d4nyll 当然,但讨论是关于撤销以前发布的 JWT,大概是在白名单上开始的,然后必须被删除才能实现撤销。
  • 这个答案对提出的问题有何用处?
【解决方案2】:

这是一个老问题,但我刚刚研究过类似的东西。所以我会在这里分享我的想法。

首先,我同意在验证 JWT 令牌的同时进行数据库调用会破坏其无状态的主要优势。

之前的答案都没有提到刷新令牌,但我相信它们在可扩展性和安全性之间提供了很好的权衡。

简而言之,可以使用具有较短到期时间(例如 15 分钟)的常规身份验证令牌和具有长期访问权限(例如 2 周)的刷新令牌。每当授权令牌过期时,刷新令牌(存储更安全)用于生成新的授权令牌,而无需用户再次登录。

jti 声明最适合刷新令牌。这使您能够撤消访问权限,同时最大限度地减少进行的数据库调用次数。

假设平均用户会话为 30 分钟。如果您对常规身份验证令牌有 jti 声明,则每个 API 调用都会执行至少一个额外的数据库调用,以检查令牌是否未列入黑名单。但是,如果您仅对刷新令牌使用 jti 声明,则在 30 分钟会话期间您将仅出于身份验证目的进行 2 次数据库调用(假设每个身份验证令牌在 15 分钟后过期)。这是一个很大的不同。

关于实现,您可以使用随机生成的 UID 并将其用作表的主键。这保证了呼叫尽可能快。此外,您可以添加与exp 声明具有相同值的expiration_time 列。这样您就可以轻松(批量)删除所有过期的刷新令牌。

为什么要使用 JWT 而不是仅将随机生成的会话 ID 存储在数据库中?

或者,如果我可以解释一下,“为什么要使用 JWT 刷新令牌而不是保存在数据库中的随机字符串?”

我认为您可以这样做,但是使用 JWT 令牌至少有两个优点:(1) 如果令牌无效或过期(当您对其进行解码时),您根本不必进行任何数据库调用.您只需返回带有错误状态代码的响应。 (2) 如果您有一个大型系统,您可能会根据某些标准(例如,每个客户端应用程序 [web 与移动])将“随机字符串”存储在不同的数据库表中。你怎么知道要在哪个表中查找随机字符串?使用 JWT 令牌,您可以简单地添加 client_id 声明。因此,在令牌中包含信息的能力很有用。

【讨论】:

    【解决方案3】:

    你可以使用 express-jwt 包

    请参阅 GitHubNPM 上的 express-jwt。

    Express-jwt 处理 吊销的令牌,如下所述:https://github.com/auth0/express-jwt#revoked-tokens

    var jwt = require('express-jwt');
    var data = require('./data');
    var utilities = require('./utilities');
    
    var isRevokedCallback = function(req, payload, done){
      var issuer = payload.iss;
      var tokenId = payload.jti;
    
      data.getRevokedToken(issuer, tokenId, function(err, token){
        if (err) { return done(err); }
        return done(null, !!token);
      });
    };
    
    app.get('/protected',
      jwt({secret: shhhhhhared-secret,
        isRevoked: isRevokedCallback}),
      function(req, res) {
        if (!req.user.admin) return res.send(401);
        res.send(200);
      });
    

    您也可以阅读第 4 部分。我们如何避免增加开销?来自this oauth0 blog post

    【讨论】:

      【解决方案4】:

      虽然在大多数情况下 JWT 令牌应该离线验证,但在某些情况下在线验证可能更有意义。您的应用程序几乎肯定会公开许多类型的操作,其中一些操作比其他操作风险更大(并且可能不那么频繁)。这些有风险的操作有时可以从在线令牌验证中受益,因为在验证令牌时,使用它可能不再安全(例如,它可能已被撤销,这对于寿命较长的 JWT 尤其明显 -这反过来又可以最大程度地减少刷新压力)。另一件事是你可能有某种风险引擎解决方案,它可以决定发行代币是否是一个好的决定。

      【讨论】:

        猜你喜欢
        • 2019-03-29
        • 2021-04-30
        • 2017-11-23
        • 1970-01-01
        • 2017-08-18
        • 2020-07-31
        • 2020-01-23
        • 2020-03-31
        • 2021-04-27
        相关资源
        最近更新 更多