我们现在就是Redis

了解更多

JSON Web令牌(JWT)对用户会话是危险的——这里有一个解决方案

了解为什么JSON Web令牌(JWT)虽然很流行,但很危险,并查看一个经过战斗测试的解决方案。



回到博客

有时,人们采用旨在解决一个狭隘问题的技术,并开始广泛应用。这个问题可能看起来很相似,但利用独特的技术来解决一般问题可能会产生意想不到的后果。用一个比喻,如果你是一把锤子,一切看起来都像钉子。JWT就是这样一种技术。

来源:“停止在会话中使用JWT”(参见下面的参考链接)。
会话
来源:“为什么jwt不利于身份验证”-兰德尔DeggesOkta的开发商倡议负责人。
不违约
来源:“JWT不应该是您会话的默认设置”(参见下面的链接)。
“

Okta等公司的SME有许多深入的文章和视频,讨论使用JWT代币的潜在危险和低效性[1]. 然而,这些警告被营销人员、YouTuber、博客作者、课程创作者以及其他有意或无意推广该课程的人所掩盖。

如果你看这些视频和文章,它们都只是谈论JWT的感知好处,而忽略了它的不足。更具体地说,它们只是展示如何使用它,但没有谈论JWT在实际生产环境中增加的撤销和额外复杂性。它们也从来没有将其与现有的经过测试的产品进行比较方法的深度足以真正权衡利弊。

又或许是它完美、吸引眼球、友好的名字让它如此受欢迎。“JSON”(通常很受欢迎)、“Web”(用于Web)和“Token”(意味着无状态)让人们认为它非常适合他们的Web身份验证工作。

所以我认为这是一个市场营销打败了工程师和安全专家的案例。但是,这并不全是坏事,因为黑客新闻中经常会有关于JWT的长时间、热烈的辩论(参见在这里,E在这里),所以还有希望。

如果你仔细想想,这些不断的争论本身应该是一个危险信号,因为你永远不会看到这样的争论,特别是在安全领域。安全应该是二进制的。技术要么是安全的,要么不是。

无论如何,在这篇博文中,我想重点讨论一下使用JWT的潜在危险,并讨论一个经过十年考验的解决方案。

为了进一步理解,当我谈论JWT时,我指的是“无状态JWT”,这是JWT流行的主要原因,也是首先使用JWT的最大原因。另外,我在下面的参考资料部分列出了所有涉及JWT本质的其他文章。

在我们理解为什么它是危险的之前,让我们先通过一个示例用例来了解它是如何工作的。

用例

假设您正在使用Twitter。你登录,写一条推文,就像推文一样,然后转发别人的推文。所以你做了四个动作。对于每个操作,您都需要经过身份验证和授权才能执行该特定操作。

下面是传统方法中发生的情况。

传统的方法

  1. 你用你的用户名和密码登录:
    1. 服务器首先对用户进行身份验证。
    2. 然后,服务器创建一个会话令牌,将该令牌和用户信息存储在某个数据库中。(注意:会话令牌是一个长且无法识别的字符串,也就是opaque字符串,看起来像这样:fsaf12312dfsdf364351312srw12312312dasd1et3423r)
  2. 然后,服务器将会话令牌发送给前端移动或web应用程序。
    1. 然后,该令牌存储在cookie或应用程序的本地存储器中。
  3. 接下来,假设你写了一条推文并提交了它。然后,与你的tweet一起,你的应用还将发送会话令牌(通过cookie或头部),以便服务器可以识别你是谁。但是令牌只是一个随机字符串,那么服务器如何仅从会话令牌就知道您是谁呢?
  4. 当服务器接收到会话令牌时,它将不知道用户是谁,因此它将该令牌发送给数据库以从该令牌检索(4a)实际用户的信息(如userId)。
  5. 如果用户存在并且被允许做这个动作(例如发送一条tweet),服务器就会允许他们做这个动作。
  6. 最后,它告诉前端推文已经发送。
灰色2

传统方法的主要问题是

问题是,第四步很慢,用户执行的每一个操作都需要重复。因此,每个API调用都会导致至少两个缓慢的DB调用,这会降低总体响应时间。

有两种方法可以解决这个问题

  1. 以某种方式完全消除用户的数据库查找(即消除步骤4)。
  2. 使额外的数据库查找更快,这样额外的跃点就不重要了。

选项1:消除数据库查找(步骤4)

有不同的方法来实现这一点。

  1. 您可以将状态存储在服务器的内存中。但这会在扩展时引起问题,因为此状态仅在特定服务器上可用。
  2. 使用“粘性会话”。这里告诉负载均衡器,即使在扩展后,也要始终将流量导向特定的服务器。这同样会导致不同的可伸缩性问题,如果服务器下降(缩小),您将失去所有状态。
  3. 第三种选择是JWT。接下来我们将对此进行研究。

现在让我们看看JWT的方法。

JWT方式

JWT,特别是作为会话使用时,试图通过完全消除数据库查找来解决这个问题。

其主要思想是将用户信息存储在会话令牌本身中!因此,与其使用一些长长的随机字符串,不如将实际的用户信息存储在会话令牌中。为了保护它,使用只有服务器知道的秘密签名令牌的一部分。

因此,即使客户端和服务器可以看到令牌的用户信息部分,第二部分,即签名部分,也只能由服务器验证。

在下图中,令牌的粉红色部分包含有效负载(用户信息),客户端或服务器都可以看到。

但是蓝色部分使用一个秘密字符串、头和有效负载本身进行签名。因此,如果客户机篡改了有效负载(比如说模拟了另一个用户),则签名将不同,并且不会进行身份验证。

编码

下面是我们使用JWT时的用例:

  1. 你用你的用户名和密码登录:
    1. 服务器通过查询数据库对用户进行身份验证
    2. 然后,服务器使用用户信息和密码创建JWT会话令牌(不涉及DB)
  2. 然后服务器向前端应用程序发送JWT令牌。对于将来的活动,用户只需发送JWT令牌来标识用户,而无需每次登录。
    1. JWT令牌如下所示:
  3. 接下来,假设您编写并提交了一条推文。当您发送推文时,您的应用程序还将发送JWT令牌(通过cookie或标头),以便服务器能够识别您是谁。但服务器如何从JWT令牌中知道您是谁?嗯,令牌的一部分已经包含用户信息。
  4. 因此,当服务器接收到JWT令牌时,它将使用机密验证签名部分,并从有效负载部分获取用户信息。从而消除了数据库调用。
  5. 如果签名签出,就允许他们执行操作。
  6. 最后发送消息被保存的前端

对于每个用户操作,服务器只需验证已签名的部分,获取用户信息,并允许用户执行该操作,从而完全跳过DB调用。

灰色1

令牌过期

但是关于JWT令牌还有一件很重要的事情需要知道。那就是它使用一个过期时间来让自己过期。它通常被设置为5分钟到30分钟。而且因为它是自包含的,所以你不能轻易地撤销/失效/更新它。这才是问题的症结所在。

那么,为什么JWT对于用户身份验证是危险的呢?

JWT的最大问题是令牌失效问题。因为它会一直工作到过期,所以服务器没有简单的方法来撤销它。

下面是一些会使这变得危险的用例。

  1. 注销并不是真的让你注销!

想象一下,你在推特后从推特上注销。您可能认为您已从服务器注销,但事实并非如此。因为JWT是自包含的,将继续工作,直到到期。这可能是5分钟或30分钟,或者是作为令牌一部分设置的任何持续时间。因此,如果有人在这段时间内访问了该令牌,他们可以继续访问它,直到它过期。

  1. 阻止用户不会立即阻止他们。

假设你是Twitter或一些在线实时游戏的管理员,而真正的用户正在使用该系统。作为版主,你希望迅速阻止某人滥用系统。你不能,同样的原因。即使在您阻止之后,用户仍将继续访问服务器,直到令牌过期。

  1. 可能有过时的数据

假设该用户是管理员,并被降级为具有较少权限的普通用户。同样,这不会立即生效,用户将继续作为管理员,直到令牌过期。

  1. JWT通常不加密,因此任何能够执行中间人攻击并嗅探JWT的人现在都拥有您的身份验证凭据。这变得更容易,因为MITM攻击只需要在服务器和客户端之间的连接上完成。

其他复杂性和考虑事项

库和规范问题:

我们发现,许多实现JWT的库多年来都存在许多安全问题,甚至规范本身也存在安全问题。甚至Auth0本身,谁宣传JWT遇到了问题

令牌的长度:

在许多复杂的现实世界应用程序中,您可能需要存储大量不同的信息。将其存储在JWT令牌中可能会超过允许的URL长度或cookie长度,从而导致问题。此外,您现在可能会在每个请求上发送大量数据。

无论如何都需要维护状态(对于速率限制、IP白名单等)

在许多现实世界的应用程序中,服务器必须维护用户的IP并跟踪API以进行速率限制和IP白名单。因此,无论如何,你都需要使用速度极快的数据库。认为你的应用程序在JWT中变得无状态是不现实的。

建议的解决办法

一种流行的解决方案是在数据库中存储一个“已撤销令牌”列表,并在每次调用时对其进行检查。如果令牌是已撤销列表的一部分,则阻止用户执行下一个操作。但是现在您正在向DB进行额外的调用,以检查令牌是否被撤销,从而完全欺骗了JWT的目的。

要旨

尽管JWT确实消除了数据库查找,但在此过程中引入了安全问题和其他复杂性。安全性是二进制的——要么安全,要么不安全。因此,将JWT用于用户会话是很危险的。

我在哪里可以使用它?

在某些情况下,您在后端进行服务器到服务器(或微服务到微服务)通信,一个服务可能会生成JWT令牌,将其发送到另一个服务以进行授权和其他狭窄的位置,例如重置密码,您可以将JWT令牌作为一次性短期令牌发送,以验证用户的电子邮件。

如果我不能使用JWT,我还能做什么?

解决方案是完全不将JWT用于会话目的。相反,用传统的,经过实战检验的方法更有效。

也就是说,使数据库查找速度非常快(亚毫秒),以至于额外的调用无关紧要。

选项2:快速查找,使其不再重要(经过战斗测试的解决方案)

有没有数据库速度如此之快,以至于可以在亚毫秒内处理数百万个请求?

当然有。它叫Redis!每天为数十亿用户提供服务的数千家公司正是为了这个目的而使用Redis!

Redi万博体育彩s Enterprise是Redis OSS的增强版,提供99.999%的可用性,可以服务数万亿次请求。它可以作为私有云上的自由软件使用,也可以作为前三大云中任何一个云上的自由软件使用。万博电竞客服

还有什么?Redis En万博体育彩terprise现在已经从仅仅是一个缓存或会话存储演变为一个功能完备的多模型数据库,并具有模块生态系统它与core Redis本机运行。例如,您可以使用RedisJSON(快10倍和市场领先者),本质上有一个实时的类似mongodb的数据库,或者使用再搜索模块(快4-100倍)并实现像Algolia一样的实时全文搜索。

如果你只是简单地使用Redis作为会话存储和一些其他数据库作为主数据库,这就是你的架构看起来的样子。需要注意的一点是Redis Enterprise提供万博体育彩的四种类型的缓存:缓存旁(延迟加载)、写后(回写)、直写和读副本,不同于Redis OSS只提供一个(缓存旁)。

注意,闪电表情符号表示速度非常快。蜗牛表情符号表示速度很慢。

会话JWT

如前所述,您还可以将Redis用作整个数据层的主数据库。在这个场景中,您的体系结构变得更加简单,基本上,一切都变得非常快速。

Db JWT

它的规模吗?

当然,公司不仅将Redis用作独立数据库,还将其用作地理分布数据库集群。

[1]引用:

  1. 停止在会话中使用JWT
  2. JWT不应该是会话的默认值
  3. 为什么JWT不利于身份验证–Randall Degges(Okta开发关系负责人)
  4. 停止在会话中使用JWT,第2部分:解决方案不起作用的原因
  5. 托马斯·H·普塔切克谈黑客新闻
  6. 我使用JSON Web令牌的经验
  7. Web身份验证(会话、Cookie、JWT、本地存储等)
  8. 托马斯·H·普塔切克的博客

Baidu