无效的JSON Web令牌

2020/09/27 06:21 · javascript ·  · 0评论

对于我正在研究的一个新的node.js项目,我正在考虑从基于cookie的会话方法切换(这意味着,将ID存储到包含在用户浏览器中的用户会话的键值存储中)到使用JSON Web令牌(jwt)的基于令牌的会话方法(无键值存储)。

该项目是一个利用socket.io的游戏-在单个会话(web和socket.io)中会有多个通信渠道的情况下,使用基于令牌的会话将非常有用。

如何使用jwt方法从服务器提供令牌/会话无效?

我还想了解我应该用这种范例寻找哪些常见(或不常见)的陷阱/攻击。例如,如果此范例易受与基于会话存储/ Cookie的方法相同/不同类型的攻击的影响。

因此,说我有以下内容(适应了thisthis):

会话商店登录:

app.get('/login', function(request, response) {
    var user = {username: request.body.username, password: request.body.password };
    // Validate somehow
    validate(user, function(isValid, profile) {
        // Create session token
        var token= createSessionToken();

        // Add to a key-value database
        KeyValueStore.add({token: {userid: profile.id, expiresInMinutes: 60}});

        // The client should save this session token in a cookie
        response.json({sessionToken: token});
    });
}

基于令牌的登录:

var jwt = require('jsonwebtoken');
app.get('/login', function(request, response) {
    var user = {username: request.body.username, password: request.body.password };
    // Validate somehow
    validate(user, function(isValid, profile) {
        var token = jwt.sign(profile, 'My Super Secret', {expiresInMinutes: 60});
        response.json({token: token});
    });
}

-

要注销(或使会话存储方法无效),将需要使用指定的令牌更新KeyValueStore数据库。

在基于令牌的方法中似乎不存在这种机制,因为令牌本身将包含通常存在于键值存储中的信息。

我也一直在研究这个问题,尽管以下所有想法都不是完整的解决方案,但它们可能会帮助其他人排除想法或提供其他想法。

1)只需从客户端删除令牌

显然,这对服务器端的安全性没有任何作用,但是通过删除令牌的存在确实阻止了攻击者(即,他们必须在注销之前窃取令牌)。

2)创建一个令牌阻止列表

您可以存储无效令牌,直到它们的初始到期日期,然后将它们与传入请求进行比较。不过,这似乎可以消除完全基于令牌的原因,因为您需要为每个请求触摸数据库。但是,存储空间可能会更小,因为您只需要存储注销和到期时间之间的令牌(这是一种直觉,并且绝对取决于上下文)。

3)保持令牌的有效期限短并经常轮换

如果您将令牌的到期时间保持在足够短的时间间隔内,并让运行中的客户端在必要时跟踪并请求更新,则数字1将有效地用作完整的注销系统。此方法的问题在于,它使得无法在关闭客户端代码之间保持用户登录状态(取决于您设置到期间隔的时间)。

临时计划

如果发生紧急情况或用户令牌遭到破坏,您可以做的一件事是允许用户使用其登录凭据更改基础用户查找ID。这将使所有关联的令牌无效,因为将不再能够找到关联的用户。

我还想指出,在令牌中包含上次登录日期是个好主意,这样您就可以在很长一段时间后强制重新登录。

关于使用令牌进行攻击的相似性/差异性,本文讨论了以下问题:https : //github.com/dentarg/blog/blob/master/_posts/2014-01-07-angularjs-authentication-with-cookies -vs-token.markdown

上面发布的想法很好,但是使所有现有JWT失效的非常简单的方法就是更改秘密。

如果您的服务器创建了JWT,并用密钥(JWS)对其进行签名,然后将其发送给客户端,则只需更改密钥即可使所有现有令牌失效,并要求所有用户获得新令牌进行身份验证,因为他们的旧令牌突然变为无效。到服务器。

它不需要对实际令牌内容(或查找ID)进行任何修改。

显然,这仅适用于紧急情况,当您希望所有现有令牌都到期时,因为每个令牌到期都需要上述一种解决方案(例如较短的令牌到期时间或使令牌内的存储密钥无效)。

这主要是一篇很长的评论,支持并基于@mattway的答案

鉴于:

此页面上其他一些建议的解决方案主张在每次请求时都访问数据存储。如果您访问主数据存储区以验证每个身份验证请求,那么我看到使用JWT代替其他已建立的令牌身份验证机制的理由就更少了。实际上,您已经使JWT成为有状态的,而不是每次都访问数据存储时都变为无状态的。

(如果您的站点收到大量未经授权的请求,那么JWT会拒绝它们而不访问数据存储,这很有用。可能还有其他用例。)

鉴于:

对于典型的现实世界Web应用程序,无法实现真正​​的无状态JWT身份验证,因为无状态JWT无法为以下重要用例提供即时安全的支持:

用户的帐户被删除/阻止/暂停。

用户密码已更改。

用户的角色或权限已更改。

用户由管理员注销。

JWT令牌中的任何其他应用程序关键数据都由站点管理员更改。

在这些情况下,您不能等待令牌到期。令牌失效必须立即发生。此外,无论是否出于恶意目的,您都不能信任客户端不要保留和使用旧令牌的副本。

因此:我认为@ matt-way#2 TokenBlackList的答案将是将所需状态添加到基于JWT的身份验证的最有效方法。

您有一个黑名单,保留着这些令牌,直到它们的到期日期。与用户总数相比,令牌列表将非常小,因为它只需要保留列入黑名单的令牌,直到令牌到期为止。我可以通过将无效令牌放置在redis,memcached或另一个支持在密钥中设置过期时间的内存数据存储中来实现。

对于通过初始JWT身份验证的每个身份验证请求,您仍然必须对内存数据库进行调用,但是不必在其中存储整个用户组的密钥。(对于给定的网站,这可能不重要)

我会在用户模型上记录jwt版本号。新的jwt令牌会将其版本设置为此。

验证jwt时,只需检查其版本号是否等于用户当前的jwt版本。

任何时候您想使旧的jwts失效,只需增加用户的jwt版本号即可。

还没有尝试过,它基于其他一些答案使用了大量信息。这里的复杂性是避免对用户信息请求的每次服务器端数据存储调用。其他大多数解决方案都需要针对用户会话存储的每个请求进行数据库查找。在某些情况下这很好,但这是为了避免此类调用并使所需的服务器端状态变得很小而创建的。您将最终重新创建一个服务器端会话,无论它多么小以提供所有强制失效功能。但是,如果您要这样做,这里是要点:

目标:

  • 减少数据存储的使用(无状态)。
  • 能够强制注销所有用户。
  • 可以随时强制注销任何个人。
  • 一定时间后要求重新输入密码的能力。
  • 与多个客户合作的能力。
  • 当用户单击特定客户端的注销时,可以强制重新登录。(为防止有人在用户离开后“取消删除”客户令牌,请参阅注释以获取更多信息)

解决方案:

  • 将寿命短(<5m)的访问令牌与寿命长(数小时)的客户端存储的更新令牌配对使用
  • 每个请求都会检查auth或刷新令牌的到期日期是否有效。
  • 当访问令牌过期时,客户端将使用刷新令牌来刷新访问令牌。
  • 在刷新令牌检查期间,服务器会检查一小列用户ID黑名单-如果找到,则拒绝刷新请求。
  • 当客户端没有有效的(未过期)刷新或身份验证令牌时,用户必须重新登录,因为所有其他请求都将被拒绝。
  • 在登录请求时,检查用户数据存储是否被禁止。
  • 注销时-将用户添加到会话黑名单中,这样他们就必须重新登录。您必须存储其他信息,以免他们在多设备环境中从所有设备中注销,但是可以通过将设备字段添加到会话中来完成。用户黑名单。
  • 要在x倍的时间后强制重新输入-请在auth令牌中保留上次登录日期,然后根据请求进行检查。
  • 要强制注销所有用户-重置令牌哈希键。

假设用户表包含禁止的用户信息,这需要您在服务器上维护黑名单(状态)。无效的会话黑名单-是用户ID的列表。仅在刷新令牌请求期间检查此黑名单。只要刷新令牌TTL,条目就必须存在。刷新令牌过期后,将要求用户重新登录。

缺点:

  • 仍然需要对刷新令牌请求进行数据存储查找。
  • 无效令牌可能会继续为访问令牌的TTL操作。

优点:

  • 提供所需的功能。
  • 在正常操作下,对用户隐藏了刷新令牌操作。
  • 仅需要对刷新请求(而不是每个请求)进行数据存储查找。即每15分钟1次,而不是每秒1次。
  • 将服务器端状态最小化到很小的黑名单。

使用此解决方案,不需要像reddis这样的内存数据存储,至少不需要用户信息,因为服务器仅每15分钟左右进行一次db调用。如果使用reddis,在其中存储有效/无效的会话列表将是一个非常快速和简单的解决方案。无需刷新令牌。每个身份验证令牌将具有一个会话ID和设备ID,它们可以在创建时存储在reddis表中,并在适当时失效。然后,将对每个请求进行检查,并在无效时将其拒绝。

我一直在考虑的一种方法是iat在JWT中始终具有一个值(发布于)。然后,当用户注销时,将该时间戳记存储在用户记录中。验证JWT时,只需将其iat与上次注销的时间戳进行比较即可。如果iat较旧,则无效。是的,您必须去数据库,但是如果JWT有效,无论如何我都会一直拉用户记录。

我看到的主要缺点是,如果它们在多个浏览器中,或者也有移动客户端,则将其从所有会话中注销。

这对于使系统中的所有JWT无效也是一种很好的机制。检查的一部分可能与最后一个有效iat时间的全局时间戳相对

我在这里有点晚,但是我认为我有一个不错的解决方案。

我的数据库中有一个“ last_password_change”列,该列存储上次更改密码的日期和时间。我还将发布的日期/时间存储在JWT中。验证令牌时,我检查密码在颁发令牌后是否已更改,并且即使令牌尚未过期也被拒绝。

您可以在数据库中的用户文档/记录上拥有一个“ last_key_used”字段。

当用户与用户一起登录并通过时,生成一个新的随机字符串,将其存储在last_key_used字段中,并在对令牌进行签名时将其添加到有效负载中。

当用户使用令牌登录时,请检查数据库中的last_key_used以匹配令牌中的last_key_us。

然后,例如,当用户注销时,或者如果您想使令牌无效,只需将“ last_key_used”字段更改为另一个随机值,任何后续检查都将失败,从而迫使用户与用户一起登录并再次通过。

------------------------回答这个问题有点晚了,但也许对某人会有帮助------------- -----------

从客户端,最简单的方法是从浏览器的存储中删除令牌。

但是,如果您想销毁节点服务器上的令牌,该怎么办-

JWT软件包的问题在于它没有提供任何破坏令牌的方法或方式。关于JWT,您可以使用上述不同的方法。但是在这里,我选择了jwt-redis。

因此,为了销毁服务器端的令牌,您可以使用jwt-redis包而不是JWT

这个库(jwt-redis)完全重复了库jsonwebtoken的全部功能,但有一个重要的补充。Jwt-redis允许您将令牌标签存储在redis中以验证有效性。Redis中没有令牌标签会使令牌无效。要销毁jwt-redis中的令牌,有一种destroy方法

它以这种方式工作:

1)从npm安装jwt-redis

2)创建-

var redis = require('redis');
var JWTR =  require('jwt-redis').default;
var redisClient = redis.createClient();
var jwtr = new JWTR(redisClient);

jwtr.sign(payload, secret)
    .then((token)=>{
            // your code
    })
    .catch((error)=>{
            // error handling
    });

3)验证 -

jwtr.verify(token, secret);

4)摧毁 -

jwtr.destroy(token)

注意:您可以在令牌登录期间提供expiresIn,方法与JWT中提供的相同。

可能对某人有帮助

为什么不只使用jti声明(立即)并将其作为用户记录字段存储在列表中(取决于数据库,但至少用逗号分隔的列表就可以了)?无需单独查找,就像其他人指出的那样,无论如何您都想获取用户记录,因此您可以为不同的客户端实例使用多个有效令牌(“注销到处”可以将列表重置为空)

保留这样的内存列表

user_id   revoke_tokens_issued_before
-------------------------------------
123       2018-07-02T15:55:33
567       2018-07-01T12:34:21

如果您的令牌在一周内到期,请清除或忽略早于该记录的记录。还仅保留每个用户的最新记录。列表的大小取决于您保留令牌的时间以及用户吊销令牌的频率。仅在表更改时才使用db。应用程序启动时将表加载到内存中。

  1. 给出令牌的1天到期时间
  2. 维持每日黑名单。
  3. 将无效/注销令牌放入黑名单

对于令牌验证,如果令牌未过期,请先检查令牌的过期时间,然后再检查黑名单。

对于长时间的会话需求,应该有一种延长令牌到期时间的机制。

晚了晚会,经过一些研究,下面给了我两美分。注销期间,请确保正在发生以下事情...

清除客户端存储/会话

每当分别发生登录或注销时,更新用户表的上次登录日期时间和注销日期时间。因此,登录日期时间应始终大于注销时间(或者,如果当前状态为登录且尚未注销,则使注销日期为null)

这比保留额外的黑名单表并定期清除要简单得多。多设备支持需要附加表来保持登录状态,登出日期以及一些其他详细信息,例如操作系统或客户端详细信息。

每个用户字符串唯一,并且全局字符串散列在一起

充当JWT机密部分允许单独和全局令牌无效。在请求身份验证期间以数据库查找/读取为代价的最大灵活性。由于它们很少更改,因此也易于缓存。

这是一个例子:

HEADER:ALGORITHM & TOKEN TYPE

{
  "alg": "HS256",
  "typ": "JWT"
}
PAYLOAD:DATA

{
  "sub": "1234567890",
  "some": "data",
  "iat": 1516239022
}
VERIFY SIGNATURE

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload), 
  HMACSHA256('perUserString'+'globalString')
)

where HMACSHA256 is your local crypto sha256
  nodejs 
    import sha256 from 'crypto-js/sha256';
    sha256(message);

有关用法的示例,请参见https://jwt.io(不确定它们是否处理动态256位机密)

我是通过以下方式做到的:

  1. 生成一个unique hash,然后将其存储在redis和您的JWT中这可以称为会话
    • 我们还将存储特定JWT发出请求-每次将jwt发送到服务器时,我们将请求整数递增(这是可选的)

因此,当用户登录时,将创建唯一的哈希,将其存储在redis中并注入到您的JWT中

当用户尝试访问受保护的端点时,您将从JWT中获取唯一的会话哈希,请查询redis并查看是否匹配!

我们可以扩展这一点,并使我们的JWT更加安全,方法如下:

每个X请求特定JWT发出的请求,我们都会生成一个新的唯一会话,将其存储在JWT中,然后将上一个会话列入黑名单。

这意味着JWT不断变化,并停止了过时的JWT被黑客入侵,被盗等。

如果您希望能够撤消用户令牌,则可以跟踪数据库中所有已颁发的令牌,并在类似会话的表上检查它们是否有效(存在)。缺点是您会在每次请求时访问数据库。

我没有尝试过,但是我建议使用以下方法允许令牌吊销,同时将数据库命中率保持在最低水平-

为了降低数据库检查率,根据确定性关联将所有已发行的JWT令牌划分为X组(例如,由用户ID的第一位数字组成10组)。

每个JWT令牌将保存组ID和创建令牌时创建的时间戳。例如,{ "group_id": 1, "timestamp": 1551861473716 }

服务器将所有组ID保留在内存中,并且每个组都有一个时间戳,该时间戳指示何时是属于该组的用户的最后一次注销事件。例如,{ "group1": 1551861473714, "group2": 1551861487293, ... }

具有JWT令牌且组时间戳较旧的请求将进行有效性检查(数据库命中),如果有效,则将发出具有新时间戳的新JWT令牌以供客户将来使用。如果令牌的组时间戳是新的,则我们信任JWT(无数据库命中)。

所以-

  1. 仅当令牌具有旧的组时间戳时,我们才使用DB验证JWT令牌,而将来的请求只有在用户组中的某人注销后才会得到验证。
  2. 我们使用组来限制时间戳更改的次数(例如,有用户登录和退出,就像没有明天一样-仅会影响有限数量的用户,而不是每个用户)
  3. 我们限制组的数量以限制保存在内存中的时间戳数量
  4. 使令牌失效很容易-只需将其从会话表中删除并为用户组生成新的时间戳即可。

如果“从所有设备注销”选项是可接受的(在大多数情况下是这样):

  • 将令牌版本字段添加到用户记录。
  • 将此字段中的值添加到JWT中存储的声明中。
  • 每次用户注销时增加版本。
  • 验证令牌时,将其版本声明与存储在用户记录中的版本进行比较,如果不相同,则拒绝。

无论如何,在大多数情况下都需要进行一次数据库访问以获取用户记录,因此这不会增加验证过程的开销。与维护黑名单不同,在黑名单中,由于需要使用联接或单独的调用,因此数据库负载很大,清理旧记录等。

以下方法可以提供两全其美的解决方案:

让“立即”表示“〜1分钟”。

情况:

  1. 用户尝试成功登录:

    答:在令牌中添加一个“发出时间”字段,并根据需要保留到期时间。

    B.存储用户密码哈希值的哈希值,或在用户表中创建一个新字段,例如tokenhash令牌哈希存储在生成的令牌中。

  2. 用户访问网址:

    答:如果“发出时间”在“立即”范围内,请正常处理令牌。不要更改“发出时间”。取决于“立即”的持续时间,这就是一个人处于脆弱状态的持续时间。但是,像一两分钟这样的短持续时间应该不太冒险。(这是性能和安全性之间的平衡)。三是不需要点击这里的数据库。

    B.如果令牌不在“立即”范围内,请对照数据库检查令牌哈希如果还可以,请更新“发出时间”字段。如果不正确,则不要处理该请求(安全性最终得以实施)。

  3. 用户更改令牌哈希以保护帐户。在“立即”的将来,该帐户将受到保护。

我们将数据库查询保存在“立即”范围内。如果在“立即”持续时间内客户端发出大量请求,这将是最有益的。

卡夫卡消息队列和本地黑名单

我考虑过使用像kafka这样的消息传递系统。让我解释:

例如,您可能有一个微服务(将其称为userMgmtMs服务),该微服务负责loginand logout并生成JWT令牌。然后,此令牌传递给客户端。

现在,客户可以使用此令牌来调用不同的微服务(将其称为pricesMs),在priceMs内,将不会对users触发初始令牌创建进行数据库检查该数据库仅存在于userMgmtMs中。此外,JWT令牌应包含权限/角色,以便priceMs无需从数据库中查找任何内容即可使spring安全性正常工作。

JwtRequestFilter可以提供一个由JWT令牌中提供的数据创建的UserDetails对象(显然没有密码),而不是使用priceMs进入数据库。

那么,如何注销令牌或使令牌无效?由于我们不想在每次请求priecesMs时都调用userMgmtMs数据库(这会引入很多不必要的依赖关系),因此一种解决方案是使用此令牌黑名单。

我建议不要使用黑名单,而是要依赖所有微服务的一个表,而建议使用kafka消息队列。

userMgmtM仍然负责logout,一旦完成,它将放入自己的黑名单(微服务之间不共享的表)。另外,它将带有此令牌内容的kafka事件发送到内部kafka服务,所有其他微服务都已预订到其中。

一旦其他微服务接收到kafka事件,它们也会将其放入内部黑名单中。

即使某些微服务在注销时已关闭,它们最终仍将再次启动,并在以后的状态下接收消息。

由于kafka的开发是为了让客户有自己的参考,即他们阅读过哪些消息,因此可以确保没有任何客户(不管是up还是up)都不会丢失任何这种无效令牌。

我能想到的唯一一个问题是,kafka消息传递服务将再次引入单点故障。但这是相反的,因为如果我们有一个全局表,其中保存了所有无效的JWT令牌,并且此数据库或微服务已关闭,则无效。使用kafka方法+对于普通用户注销,客户端删除JWT令牌后,在大多数情况下,kafka的停机时间甚至不会引起注意。由于黑名单是作为内部副本分布在所有微服务之间的。

在关闭的情况下,您需要使被黑客入侵的用户无效并且kafka处于关闭状态,这就是问题开始的地方。在这种情况下,将秘密作为万不得已的方法可能会有所帮助。或者只是在进行操作前确保kafka已经启动。

免责声明:我尚未实现此解决方案,但是以某种方式我觉得大多数提议的解决方案都通过中央数据库查找来否定JWT令牌的想法。所以我在考虑另一种解决方案。

请让我知道您的想法,这是否有意义,或者是否有明显的原因使其无法做到?

我要回答的是,当我们使用JWT时是否需要从所有设备功能中注销。这种方法将对每个请求使用数据库查找。因为即使服务器崩溃,我们也需要持久性安全状态。在用户表中,我们将有两列

  1. LastValidTime(默认值:创建时间)
  2. 已登录(默认:true)

每当用户发出注销请求时,我们都会将LastValidTime更新为当前时间,并将Logged-In更新为false。如果有登录请求,我们将不会更改LastValidTime,但Logged-In将设置为true。

当我们创建JWT时,我们将在有效负载中包含JWT创建时间。当我们授权服务时,我们将检查3个条件

  1. JWT有效吗
  2. JWT有效负载创建时间是否大于User LastValidTime
  3. 用户是否已登录

让我们看一个实际的场景。

用户X有两个设备A,B。他在晚上7点使用设备A和设备B登录到我们的服务器。(假设JWT的到期时间为12小时)。A和B都具有创建时间为7pm的JWT

晚上9点,他丢失了设备B。他立即从设备A注销。这意味着现在数据库X用户条目的LastValidTime为“ ThatDate:9:00:xx:xxx”,而已登录为“ false”。

在9:30,Thief先生尝试使用设备B登录。即使Logged-In为false,我们也会检查数据库,因此我们将不允许。

在晚上10点,X先生从他的设备A登录。现在设备A的JWT创建时间为:晚上10点。现在,数据库“登录”设置为“ true”

在晚上10:30,Thief先生尝试登录。即使Logged-In为true,也是如此。数据库中的LastValidTime是晚上9点,但是B的JWT已将时间创建为晚上7点。因此,将不允许他访问该服务。因此,使用没有密码的设备B后,一台设备注销后,他将无法使用已创建的JWT。

IAM解决方案,例如Keycloak(我已经研究过)提供了令牌撤销端点,例如

令牌撤销端点
/realms/{realm-name}/protocol/openid-connect/revoke

如果只想注销一个useragent(或一个用户),则也可以调用一个端点(这将使令牌无效)。同样,对于Keycloak,依赖方只需要呼叫端点

/realms/{realm-name}/protocol/openid-connect/logout

如果您想了解更多,请链接

另一种选择是使用仅用于关键API端点的中间件脚本。

如果管理员使令牌无效,则该中间件脚本将检入数据库。


对于不需要立即完全阻止用户访问的情况,此解决方案可能很有用。

在此示例中,我假设最终用户也有一个帐户。如果不是这种情况,则其余方法不太可能起作用。

创建JWT时,将其持久保存在与登录帐户关联的数据库中。这确实意味着仅从JWT中就可以提取有关用户的其他信息,因此,根据环境的不同,可能不同,也可能不同。可以

在之后的每个请求中,您不仅要执行所使用的框架附带的标准验证(我希望)(验证JWT是否有效),还包括诸如用户ID或其他令牌之类的东西(需要匹配在数据库中)。

注销时,删除cookie(如果使用),并使数据库中的JWT(字符串)无效。如果无法从客户端删除cookie,那么至少注销过程将确保令牌被销毁。

我发现这种方法与另一个唯一的标识符(因此数据库中有2个持久项并可供前端使用)的会话非常灵活

使用JWT的刷新...

我实际采用的一种方法是在数据库中存储刷新令牌(可以是GUID)和对应的刷新令牌ID(无论完成多少刷新都不会更改),并将它们添加为对数据库的声明。生成用户的JWT时的用户。可以使用数据库的替代方法,例如内存缓存。但是我在这个答案中使用数据库。

然后,创建一个JWT刷新Web API端点,客户端可以在JWT到期之前调用它。调用刷新时,请从JWT中的声明中获取刷新令牌。

在对JWT刷新端点的任何调用上,在数据库上将当前刷新令牌和刷新令牌ID验证为一对。生成一个新的刷新令牌,并使用该刷新令牌ID来替换数据库上的旧刷新令牌。请记住,它们是可以从JWT中提取的声明

从当前的JWT中提取用户的声明。开始生成新的JWT的过程。用新生成的刷新令牌替换旧的刷新令牌声明的值,该刷新令牌也已新保存在数据库中。有了这些,生成新的JWT并将其发送给客户端。

因此,在使用刷新令牌后,无论是由目标用户还是攻击者使用,任何其他尝试使用数据库上未与其配对的刷新令牌及其刷新令牌ID的其他尝试都不会导致生成新的JWT,因此将阻止具有该刷新令牌ID的任何客户端再使用后端,从而导致此类客户端(包括合法客户端)的完全注销。

那解释了基本信息。

接下来要添加的内容是有一个窗口,用于刷新JWT的时间,以便该窗口之外的任何事物都是可疑活动。例如,窗口可以是JWT到期前的10分钟。生成JWT的日期时间可以保存为该JWT本身中的声明。并且当发生此类可疑活动时,即当其他人尝试在窗口内或窗口内重新使用该刷新令牌ID后,应将刷新令牌ID标记为无效。因此,即使刷新令牌ID的有效所有者也必须重新登录。

在数据库上找不到与提供的刷新令牌ID配对的刷新令牌意味着该刷新令牌ID应该无效。因为闲置的用户可能会尝试使用例如攻击者已使用的刷新令牌。

如先前所述,在目标用户之前被攻击者窃取并使用的JWT,在用户也尝试使用刷新令牌时,也会被标记为无效。

唯一无法解决的情况是,即使攻击者可能已经窃取了客户端,客户端也从未尝试刷新其JWT。但是,这种情况不太可能发生在不受攻击者监管的客户端上,这意味着攻击者无法预测客户端何时停止使用后端。

如果客户端启动常规注销。应该注销以从数据库中删除刷新令牌ID和相关记录,因此,可以防止任何客户端生成刷新JWT。

无需使用JWT的刷新...

我想到了两种攻击情形。一种是关于受损的登录凭据。另一个是JWT的实际盗窃。

对于受损的登录凭据,通常在发生新登录时向用户发送电子邮件通知。因此,如果客户不同意登录,则应建议他们重置凭据,这应该将密码的上次设置日期保存到数据库/缓存中(在以下情况下也要进行设置)用户在初始注册时设置密码)。只要授权了用户操作,就应该从数据库/缓存中获取用户更改密码日期时间,并将其与生成给定JWT的日期时间进行比较,并禁止对在上述凭证日期时间重置之前生成的JWT进行操作,因此实际上使此类JWT无法使用。这意味着将JWT的生成日期时间保存为JWT本身的声明。在ASP.NET Core中,可以使用策略/要求进行此比较,并且在失败时,将禁止客户端。因此,每当凭据重置完成时,就会在全局范围内注销用户。

对于JWT的实际盗窃... JWT盗窃并不容易检测,但是过期的JWT可以轻松解决此问题。但是,在JWT到期之前可以采取什么措施阻止攻击者呢?这是实际的全局注销。它类似于上面针对凭证重置所述的内容。为此,通常将用户启动全局注销的日期时间保存在数据库/缓存中,并在授权用户操作时将其获取,并将其与给定JWT的生成日期时间进行比较,并禁止该操作用于在上述全局注销日期时间之前生成的JWT,因此实际上使此类JWT变得无用。如前所述,可以使用ASP.NET Core中的策略/要求来完成此操作。

现在,您如何检测JWT的盗窃案?我现在的答案是偶尔提醒用户全局注销并再次登录,因为这肯定会使攻击者注销。

如果没有在每个令牌验证中都进行数据库查找,这似乎很难解决。我能想到的替代方法是在服务器端保留一个无效令牌的黑名单。每当发生更改以在重新启动期间持久保留更改时,都应在数据库上更新该更新,方法是使服务器在重新启动时检查数据库以加载当前黑名单。

但是,如果将其保存在服务器内存中(某种全局变量),则如果使用多个服务器,则无法在多台服务器上进行扩展,因此在这种情况下,可以将其保存在共享的Redis缓存中,这应该是设置以将数据保留在某个地方(数据库,文件系统?),以防万一必须重新启动它,并且每次启动新服务器时,都必须订阅Redis缓存。

替代黑名单,使用相同的解决方案,您可以使用每个会话在redis中保存的哈希值来实现此目的,正如其他答案所指出的那样(不确定如果许多用户登录,这样做会更有效)。

听起来很复杂吗?它对我有用!

免责声明:我没有使用过Redis。

如果您正在使用axios或类似的基于promise的http请求库,则只需破坏.then()部件内部前端的令牌即可用户执行此功能后,它将在response .then()部分中启动(来自服务器端点的结果代码必须正确,200)。用户在搜索数据时单击此路由后,如果数据库字段user_enabled为false,它将触发销毁令牌,并且用户将立即注销并停止访问受保护的路由/页面。当用户永久登录时,我们不必等待令牌过期。

function searchForData() {   // front-end js function, user searches for the data
    // protected route, token that is sent along http request for verification
    var validToken = 'Bearer ' + whereYouStoredToken; // token stored in the browser 

    // route will trigger destroying token when user clicks and executes this func
    axios.post('/my-data', {headers: {'Authorization': validToken}})
     .then((response) => {
   // If Admin set user_enabled in the db as false, we destroy token in the browser localStorage
       if (response.data.user_enabled === false) {  // user_enabled is field in the db
           window.localStorage.clear();  // we destroy token and other credentials
       }  
    });
     .catch((e) => {
       console.log(e);
    });
}

我只是将令牌保存到用户表中,当用户登录时,我将更新新令牌,并且当auth等于用户当前的jwt时。

我认为这不是最佳解决方案,但对我有用。

本文地址:http://javascript.askforanswer.com/wuxiaodejson-weblingpai.html
文章标签: ,   ,   ,   ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!