安全且无状态的JWT实现

5

背景

我正在尝试使用JSON Web令牌在我的Web应用程序中实现令牌身份验证。

无论我最终使用什么策略,都有两件事情我想要保持:无状态和安全。然而,从阅读这个网站上的答案和互联网上的博客文章来看,似乎有一些人认为这两个属性是互相排斥的。

在试图保持无状态时会出现一些实际细节。我可以想到以下几点:

  • 在其到期日期之前按用户基础使被攻击的令牌失效。
  • 允许用户一次性注销所有机器上的所有“会话”,并立即生效。
  • 允许用户注销当前机器上的当前“会话”,并立即生效。
  • 使用户记录上的权限/角色更改立即生效。

当前策略

如果您在JWT中使用“发行时间”声明并与表示用户记录的数据库表中的“上次修改”列结合使用,则我认为可以优雅地处理上述所有问题。

当进行身份验证时,您可以查询数据库以获取用户记录,并执行以下操作:

if (token.issued_at < user.last_modified) then token_valid = false;

如果您发现有人侵犯了用户的帐户,那么用户可以更改他们的密码并更新last_modified列,从而使之前颁发的令牌失效。这也解决了权限/角色更改不立即生效的问题。
另外,如果用户要求立即注销所有设备,则需要更新last_modified列。
最后一个问题是每个设备的注销。然而,我认为这甚至不需要访问服务器,更不用说访问数据库了。注销操作是否可以触发一些客户端事件监听器以删除保存JWT的安全cookie呢?

问题

首先,你是否看到上述方法中的任何安全漏洞? 我是否忽略了某些易用性问题?
一旦解决了这个问题,我真的不喜欢每次有人访问安全终点时都要查询数据库,但这是我能想到的唯一策略。 有没有人有更好的想法?
1个回答

3
您对某些常见需求如何破坏JWT的无状态性做出了很好的分析。我只能对您目前的策略提出一些改进建议。
当前策略:
我看到的缺点是需要始终查询数据库。而用户数据的微小修改可能会更改last_modified并使令牌失效。
另一种选择是维护一个令牌黑名单。通常为每个令牌分配一个ID,但我认为您可以使用last_modified。由于吊销令牌的操作可能很少,因此您可以保持轻量级的黑名单(甚至可以在内存中缓存),其中只有userId和last_modified。
在对用户进行关键数据的更新(密码、权限等)后,只需要设置一个条目,并且currentTime - maxExpiryTime < last_login_date。当currentTime - maxExpiryTime > last_modified时可以丢弃该条目(不再发送未过期的令牌)。
能否注销只触发一些客户端事件侦听器以删除保存JWT的安全cookie?
如果您在同一浏览器上有多个打开的选项卡,则可以使用localStorage事件在选项卡之间同步信息以构建注销机制(或登录/用户更改)。如果您指的是不同的浏览器或设备,则需要从服务器向客户端发送某种事件方式。但这意味着需要维护一个活动通道,例如WebSocket,或向本机移动应用程序发送推送消息。
在上述方法中是否存在任何安全漏洞?
如果您正在使用cookie,请注意需要设置额外的保护来防范CSRF攻击。此外,如果您不需要从客户端访问cookie,请将其标记为HttpOnly
我有没有忽略任何可用性问题?
您还需要处理当令牌接近过期时旋转令牌的问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接