如何安全储存JWT之Cookie与Web Storage

5,426 阅读6分钟

一句话结论 

确保你的代码以及第三方库的代码有足够的XSS检查,在此之上将jwt存放在local storage中。

若有理解不到位之处,请在评论区留言,跪谢!

背景

每一个在自己项目中使用JWT的前端开发人员都会遇到同一个问题,我应该在哪里储存我的jwt token?

如果你搜索相关的文章你会发现主流意见基本上分为了两派:

  1. Local Storage (Web Storage中的一种)
  2. Cookie

支持Cookie的开发人员会强烈建议不要将敏感信息(例如JWT)储存在Local Storage中,因为它对于XSS毫无抵抗力,并且批判培训班或者大部分开发人员总是一股脑的选择Local Storage,而忽视了安全这个最大的问题。

支持Local Storage的一派认为,撇开Local Storage的各种优点不谈,如果做好适当的XSS防护,收益是远大于风险的。

两种论点总是能激起一番激烈的讨论,我写此文的目的就是希望对两种不同的观点进行比较,给出我自己的结论,并且提出更加细化的方案。

以下讨论会涉及基本的XSS, CSRF,Cookie 以及 Web Storage 的基本知识,如果你感到陌生,在文章的末尾有简单的介绍。

0. 先纠正一个错误观念

Cookie比Local Storage更安全

这种观点是不全面的,Local storage 有着与 Cookie一样的安全机制,只有加了httpOnly 和 secure 的Cookie才更加安全,对于普通的Cookie,它的安全等级与Local Storage并无差别。

1. JWT与Local Storage

将JWT储存在Local Storage中然后通过请求中的Authorization Header发送,注意Header不是由浏览器自动添加。

优点

  • 在笔者看来在安全方面最大的收益便是天然的CSRF免疫,因为不会被浏览器自动发送。 如果你使用了这种方法,后端并不需要花费时间精力来做CSRF相关的防护。
  • 如果你JWT较大或者你并不是Cookie的唯一使用者,那么你会觉得放在Local Storage中更加方便,毕竟Cookie的4k大小很容易用完。
  • JS可以直接读取JWT,方便使用。

缺点

  • 从安全的角度来看,如果你的网站有着XSS漏洞那么黑客可以非常轻松(只需要一行代码)的读取你的Local Storage并窃取JWT。

2. JWT与Cookie(加了httpOnly和secure)

将JWT储存在Cookie中然后通过请求中的Cookie自动由浏览器发送。

优点

  • 更加安全?黑客无法直接读取加了httpOnly Cookie中的内容,secure确保cookie只经由https传输

缺点

  • Cookie 的4K存储空间太小
  • CSRF问题

3. Cookie真的更加安全吗?

存储在Cookie中并且加了HTTP Only是否意味者黑客的代码就无法读取呢?

答案是否定的 。

虽然无法直接读取(这里假设了浏览器支持httpOnly, 这个功能在主流浏览器中是普遍支持的,但是在相对旧的版本浏览器可能并不支持),但是黑客依然可以通过HTTP trace来获取服务器返回的Cookie内容。所以在服务器端你需要将trace禁用。

如果以上两点都配置之后那么你的Cookie就是相对安全的,

CRSF?

你需要额外的配置(前端以及后端)来解决它,巧用JWT你可以轻松的实现一个无状态易于水平拓展的CRSF解决方案。例如:

将服务端随机生成的CRSF token放在jwt的claim中来做到无状态,并且CRSF token 放在响应的Header这样可以被前端读取,随后的请求中将CRSF token作为header的一部分发送。服务器只需要验证claim和header。

听起来很完美,但是应该在哪里储存这个CRSF token呢?Cookie(httpOnly不能加因为你的代码需要读取它)或者local storage ? 两者对于XSS同样脆弱。

如果不存储CSRF token呢或者存在session storage这种只对当前页面可见并且关掉就删除的地方呢?那么下次你用户打开界面或者打开一个新的页面还需要重新登录。这是我们不想看到的。假设你有多个tab打开那么通过local storage事件来共享session storage的信息呢?这样同样能被恶意代码获取。

只要你的网站存在着XSS漏洞那么CRSF型同虚设,httpOnly只能保护你的cookie,但无法让你免疫XSS。

4. XSS这个洪水猛兽

在XSS面前,即便你的httpOnly cookie无法被获取,黑客仍然可以诱导或者在用户毫不知情的情况下做任何事情。记住!黑客的代码和你的代码一样被用户信任!XSS只要存在那么无论将信息存储在cookie还是local storage,都是一样脆弱不堪,唯一的区别只是获取难度。

XSS漏洞很难被发现

因为一个网站的构建不仅仅是基于你自己的代码,第三方的代码同样以可能存在XSS漏洞。

5. 结论

确保你的代码以及第三方库的代码有足够的XSS检查,在此之上将jwt存放在local storage中。

放在cookie中看似安全,看似“解决”(因为仍然存在XSS的问题)一个问题,却引入了另一个问题(CSRF)

local storage 具有更加灵活,更大空间,天然免疫CRSF的特征。Cookie空间有限,而jwt一般都占用较多字节,而且有时你不止需要存储一个JWT。

关于页面之间的数据共享

如果用户已经在一个页面中打开了你的应用,那么当用户打开另一个页面时,我们一般希望用户可以跳过登录的环节。这个功能需要我们能够在tab之间共享数据,类似的方法有cookie,在同一个域名下。或者local storage,有着类似cookie的安全机制并且更大的存储空间。session storage无法在不同tab之间共享数据但是你可以通过local storage事件来进行传递

术语

1. XSS ( Cross site scripting )

是指黑客能够在你的网站当中执行他的代码 

2. CSRF ( cross site request forgy )

是指恶意利用浏览器会自动发送cookie的功能,由黑客所有网站发起的对于目标网站的恶意请求

3. cookie 和 web storage 特性

  1. cookie 和 local storage 都是只能被同一个域名访问
  2. cookie只能储存4K大小的数据
  3. local storage的大小根据不同浏览器而定,一般能存储5M左右的数据
  4. cookie可以通过httpOnly来确保cookie只能被用来网络传输,以及secure来保证只在HTTPS中传输
  5. local storage有事件机制,可以触发和监听事件
  6. session storage只对当前页面可见,且关闭当前页面后删除
  7. cookie如果没有明确过期时间在页面关闭时会删除,称为session cookie
  8. 有明确过期时间的cookie会在过期后删除,称为persistent cookie