您现在的位置是:首页 > 讲真,别再使用JWT了

讲真,别再使用JWT了

默认 2019-05-20 22:12 1392人围观 来源:原创
JWT   Cookie  
简介不幸的是,最近我看到越来越多的人建议使用JWT(JSON Web Tokens)来管理其Web应用程序中的用户会话。这是一个糟糕的想法,在这篇文章中,我将解释原因。

不幸的是,最近我看到越来越多的人建议使用JWT(JSON Web Tokens)来管理其Web应用程序中的用户会话。这是一个糟糕的想法,在这篇文章中,我将解释原因。

为了防止任何混淆,我将首先定义一些术语:

  • 无状态JWT:包含会话数据的JWT令牌,直接编码到令牌中。

  • 有状态JWT:仅包含会话的引用或ID的JWT令牌。会话数据存储在服务器端。

  • 会话令牌/ cookie:标准(可选签名)会话ID,就像Web框架已经使用了很长时间的形式。会话数据存储在服务器端。

要明确:本文并不认为你永远不应该使用JWT - 只是它不适合作为会话机制,并且这样使用它是危险的。在其他领域,确实存在有效的使用场景。在本文的最后,我将简要介绍其他用例。

前提注意

很多人错误地将“cookies与JWT”进行比较。这种比较完全没有意义,它就像将苹果与橙子进行比较。 cookie是一种存储机制,而JWT令牌是加密签名的令牌。

它们不是对立的 - 相反,它们可以一起使用或独立使用。正确的比较是“ session与JWT”和“cookie与local storage”。

在这篇文章中,我将把会话与JWT令牌进行比较,偶尔也会阐述“cookies vs. Local Storage”,这样做才有意义。

声称JWT的优势

当人们推荐JWT时,他们通常会声称以下一项或多项好处:

  • 更容易(水平)缩放

  • 更容易使用

  • 更灵活

  • 更安全

  • 内置过期功能

  • 无需向用户询问“cookie同意”

  • 防止CSRF

  • 在移动设备上更好用

  • 适用于阻止cookie的用户

我会分别阐述这些主张中的每一个,以及为什么它们是错误的或误导性的。下面的一些解释可能有点含糊不清; 这主要是因为声称的优势本身也是含糊不清的。我很乐意更新它以给出更具体的说法; 您可以在本文底部找到我的联系方式。

更容易(水平)缩放

这是列表中唯一在技术层面上有些正确的声明,但仅限于使用无状态 JWT令牌。然而,现实是,几乎没有人真正需要这种可扩展性。有许多更简单的扩展方法,除非你的操作规模像Reddit那么大,否则你不需要'无状态会话'。

扩展有状态会话的一些示例:

  1. 在一个服务器上运行多个后端进程:(在该服务器上)用一个Redis守护进程进行Session存储。

  2. 在多个服务器上运行:使用一个专用服务器运行Redis进行Session存储。

  3. 在多个服务器上运行,在多个集群中运行:粘性(Sticky )Session。

这些都是现有软件良好支持的场景。您的应用程序是很有可能永远不会超越第二种情况。

也许你会想,你应该开发“面向未来”的应用程序,如果你也曾经扩展超出。然而,在实践中,后续更换会话机制是相当简单的。当您进行转换时,唯一的成本是每个用户注销一次。预先实施使用JWT是不值得的,特别是考虑到我后来会遇到的缺点。

更容易使用

这点真的不是。您必须自己在客户端和服务器端处理会话管理,而标准会话cookie 只能在开箱即用的情况下工作。JWT在任何方面都不容易。

更灵活

我还没有看到有人解释JWT 如何更灵活。几乎每一个主要的Session的实现都使您可以存储任意会话数据,这和JWT的工作方式是没有区别的。据我所知,JWT只是一个流行语。如果您不同意,请随时与我联系并附带示例。

更安全

很多人认为JWT令牌“更安全”,因为他们使用加密技术。虽然带签名的cookie比未签名的cookie更安全,但这并不是JWT所独有的。好的Session实现同样也在使用签名cookie。

使用加密也并不会神奇地使某些东西更加安全。它必须服务于特定目的,并且是针对该特定目的的有效解决方案。事实上,错误使用的加密技术可能会降低安全性。

我听到的“更安全”论点的另一个解释是“它们不是作为cookie发送的”。这绝对没有意义 - 一个cookie只是一个HTTP头,并且没有什么不安全的使用cookie。实际上,cookie 对于像恶意的客户端代码此类,是极其受到良好保护的。我将在稍后介绍。

如果您担心有人拦截您的会话cookie,您应该只使用TLS - 如果您不使用TLS,任何类型的会话实现都是可以截取的,包括JWT。

内置过期功能

这是无稽之谈,而不是一个有用的功能。到期也可以在服务器端实现,许多实现都可以。事实上,服务器端到期是可取的 - 它允许您的应用程序清理它不再需要的会话数据,如果您使用有状态JWT令牌并依赖其过期机制,则无法执行此操作。

无需向用户询问“cookie同意”

完全错了。没有“cookie法律”这样的东西 - 关于cookie的各种法律实际上涵盖了任何类型的持久标识符,这些标识符对于服务的运行并非严格必需。您可以想到的任何会话机制都将由此涵盖。

简而言之:

  • 如果您使用的是Session或者Token用于功能性目的(例如,保持用户登录),那么无论你使用什么形式存储管理会话,你都不需要询问用户同意。

  • 如果您使用的是会话令牌或用于其他目的(如分析或跟踪),不管你如何存储会话,那么你都需要询问用户同意。

防止CSRF

事实并非如此。存储JWT的方法大致有两种:

  • 在cookie中:您仍然容易受到CSRF攻击,并且仍然需要对其进行保护。

  • 在其他地方,如本地存储(Local Storage):您不容易受到CSRF攻击,但您的应用程序或站点需要使用JavaScript去运行,这使得自己容易受到完全不同的,可能更糟糕的漏洞类型的攻击。下文有更多关于此的内容。

唯一正确的CSRF缓解方式是CSRF令牌。会话机制与此无关。

在移动设备上更好用

目前在使用的每个移动浏览器都支持cookie,因此也支持Session。每个主要的移动开发框架和任何重要的HTTP库都是如此。这根本不是一个问题。

适用于阻止cookie的用户

不太可能。用户不仅可以阻止cookie,还可以阻止所有持久性方法。这包括本地存储,以及允许您持久保存会话的任何其他存储机制(使用或不使用JWT)。使不使用JWT这里并不重要,这是一个完全独立的问题。 让身份验证在没有cookie的情况下工作是一个失败的原因。

最重要的是,阻止所有 cookie的用户通常可以理解为这将破坏他们的身份验证功能,并为他们关心的网站单独解锁cookie。作为Web开发人员,这个问题不是您需要去解决的; 一个更好的解决方案是向您的用户解释您的网站为何需要使用Cookie。

缺点

现在,上文已经涵盖所有声称的特性,以及为什么他们是错误的,你可能会想:“哦,这不是什么大不了的,没有关系,即使它对我并没有没什么帮助,我仍然用JWT。你错了。使用JWT作为会话机制有很多缺点,其中一些是严重的安全问题。

他们占用更多空间

JWT Tokens并不是很小。特别是在使用无状态JWT令牌时,所有数据都直接编码到令牌中,您将很快超过cookie或URL的大小限制。您可能打算将它们存储在本地存储中 - 但是......

它们不太安全

将JWT存储在cookie中时,它与任何其他Session标识符没有区别。但是当你将JWT存储在其他地方时,你很容易受到本文所述的一类新攻击(特别是“存储会话”部分)的攻击:

回到本地存储,这是一个非常棒的HTML5添加,它为浏览器和cookie添加了一个键/值存储。那么我们应该将JWT存储在本地存储中吗?考虑到这些Tokens可以达到的大小,这可能是有意义的。Cookie通常高出大约4k的存储空间。对于大型令牌,cookie可能是不可能的,本地存储将是明显的解决方案。但是,本地存储不提供cookie所执行的任何相同安全机制。

与cookie不同,本地存储不会随每个请求发送数据存储的内容。从本地存储中检索数据的唯一方法是使用JavaScript,这意味着任何提供通过内容安全策略的JavaScript的攻击者都可以访问和泄露它。不仅如此,JavaScript还不关心或跟踪数据是否通过HTTPS发送。对于JavaScript而言,存储的Tokens只是数据,浏览器将像其他任何数据一样对其进行操作。

简而言之,使用cookie并不是可选的,无论您是否使用JWT 。

您不能使单个JWT令牌无效

还有更多的安全问题。与会话不同 - 无论何时感觉都可以由服务器无效 - 个别无状态JWT令牌不能被无效。根据设计,无论发生什么情况,它们都将有效,直到它们到期为止。这意味着,在检测到危害后,您无法使攻击者的会话无效。当用户更改密码时,您也无法使旧会话无效。

你本质上是无能为力的,如果没有构建复杂的(有状态的!)基础设施来明确地检测和拒绝它们,就无法“杀死”会话,从而打败了使用无状态JWT令牌。

数据变得陈旧

与此问题有些相关,还有另一个潜在的安全问题。就像在缓存中一样,无状态令牌中的数据最终会“过期”,并且不再反映数据库中最新版本的数据。

这可能意味着,一个令牌包含像一个老的网站URL一些过时的信息,有人在他们的个人资料改变。更严重的是,它也意味着某人有一个角色的令牌admin,即使你只是吊销了其admin作用。因为你不能令牌无效,你没有办法为你免除他们的管理员权限,除非短暂关闭整个系统。

实现缺乏实战或者根本不存在

您可能会认为所有这些问题都与无状态JWT令牌有关,并且您大多数都是正确的。但是,使用有状态令牌基本上相当于常规会话cookie ...但没有经过实战考验的实现。

现有的Session实现(例如express-sessionExpress)已经在生产中运行了许多年,并且由于这个原因,它们的安全性得到了很大的改进。使用JWT令牌作为临时会话cookie时,您不会获得这些好处。您将不得不推出自己的实现(并且很可能在此过程中引入漏洞),或者使用不常见的第三方实现。

结论

无状态JWT令牌不能无效或更新,并且会根据您存储它们的位置引入大小问题或安全问题。有状态的JWT令牌在功能上与会话cookie相同,但没有经过实战考验和经过良好审核的实现或客户端支持。

除非您使用Reddit规模的应用程序,否则没有理由将JWT令牌用作会话机制,只需使用Session即可。

那么...... JWT的优点是什么呢?

在本文开头,我说JWT 有很好的使用场景,但它们不适合作为会话机制。JWT特别有效的用例通常是被用作用户一次性授权令牌。

来自JSON Web Token规范

JSON Web令牌(JWT)是一种紧凑的,URL安全的方式,用于表示在双方之间转移的声明。[...]使声明能够通过消息验证码(Message Authentication Code)进行数字签名或完整性保护和/或加密。

在这种情况下,“声明”可以是类似“命令”,一次性授权或基本上任何其他可以表达的场景:

你好服务器B,服务器A告诉我,我可以<声称在这里>,这里是(加密)证明。

例如,您可能运行文件托管服务,用户必须在其中进行身份验证才能下载文件,但文件本身由单独的无状态“下载服务器”提供服务。在这种情况下,您可能希望让您的应用程序服务器(服务器A)发出一次性“下载令牌(Token)”,然后客户端可以使用该令牌Token文件从下载服务器(服务器B)下载该文件。

以这种方式使用JWT时,有一些特定的属性:

  • 令牌是短暂的。它们只需要有效几分钟,以允许客户端启动下载。

  • 该令牌只能使用一次。应用程序服务器会为每次下载发出一个新令牌,因此任何一个令牌只用于请求一次文件,然后被丢弃。有没有持久化状态。

  • 应用程序服务器仍使用会话。只是下载服务器使用令牌来授权单个下载,因为它不需要持久状态。

正如你在这里看到的,结合Session和JWT Token是完全合理的。它们各有各的目的,有时你需要两者。不要给需要持久化、长期保存的数据使用JWT。



作者:vayci
链接:https://www.jianshu.com/p/f4b049761473
 
我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=3cn0udci9ym88

文章评论