在哪里存储你的JWT —— Cookies vs HTML5 Web存储

作者:Tom Abbott | 2016年1月8日
源文章地址:Where to Store your JWTs – Cookies vs HTML5 Web Storage

前言

要创建一个java应用吗?JJWT是一个提供端到端JWT创建和验证的Java库,由我们自己的Les Hazlewood开发。JJWT永远是免费和开源的(Apache License, Version 2.0),使用和理解都很简单。它被设计成一个以构建者为中心的连贯接口,隐藏了它的大部分复杂性。我们希望你能尝试一下,让我们知道你的想法!(如果您是Node开发人员,请查看NJWT)

Stormpath最近正在研究使用JSON Web令牌(JWT)进行令牌身份验证的特性,我们已经就这些令牌的安全性以及在何处存储它们进行了多次讨论。

如果你想对JWT进行深入了解,这篇文章就是为你准备的。我们将介绍一些基础知识关于JSON Web令牌(JWT)与OAuth的对比、token存储在cookies中与存在HTML5 Web存储(localStorage或sessionStorage)的对比,以及关于跨站点脚本编制(XSS)和跨站点请求伪造(CSRF)。

让我们开始吧…

JSON Web TOKEN(JWT):速成课程

API认证和授权最常用的解决方案是OAuth 2.0和JWT规范,它们应用十分广泛。克利夫的笔记时间!以下是你需要知道的关于JWT和OAuth的对比:

  • JWT是一种很好的身份验证机制。它们提供了一种结构化和无状态的方法来声明用户及其可以访问的内容。可以对它们进行签名和编码,以防止在客户端进行篡改。
  • JWT是声明关于token和身份验证信息的好方法。由于使用JSON,你有大量的自由来决定怎么使用它,从而让你的应用更有意义。
  • scopes背后的概念非常强大,但也非常简单:你可以自由设计自己的访问控制设计,因为你使用的是JSON。

如果你遇到一个token,它看起来是这样的:

"dBjftJeZ4CVP.mB92K27uhbUJU1p1r.wW1gFWFOEjXk…"

这是一个Base64编码的字符串。如果你把它拆开,你会发现三个独立的部分:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
.
eyJpc3MiOiJodHRwOi8vZ2FsYXhpZXMuY29tIiwiZXhwIjoxMzAwODE5MzgwLCJzY29wZXMiOlsiZXhwbG9yZXIiLCJzb2xhci1oYXJ2ZXN0ZXIiXSwic3ViIjoic3RhbmxleUBhbmRyb21lZGEuY29tIn0
.
edK9cpfKKlGtT6BqaVy4bHnk5QUsbnbYCWjBEE7wcuY

第一部分是描述token的标头。第二部分是一个有效负载,它包含丰富的信息,第三部分是一个签名散列,可以用来验证token的完整性(如果你有用于签名的密钥)。

当我们解码第二部分,有效载荷,我们得到这个完整的JSON对象:

{
  "iss": "http://galaxies.com",
  "exp": 1300819380,
  "scopes": ["explorer", "solar-harvester", "seller"],
  "sub": "tom@andromeda.com"
}

这是令牌的有效负载。它让你知道以下内容:

  • 这个人是谁(sub, subject的缩写)
  • 这个人可以使用这个令牌访问什么(范围)
  • 令牌到期时(exp)
  • 谁发行了令牌(iss,发行人的简称)

这些信息称为“声明”,因为令牌创建者声明了一组可用于“了解”有关主题的内容的断言。因为令牌是用密钥签名的,所以您可以验证它的签名并隐式地信任所声明的内容。

令牌是在用户提供一些凭证(通常是用户名和密码)之后提供给用户的,但是它们也可以提供API密钥,甚至来自其他服务的令牌。这一点很重要,因为向API传递令牌(可能过期,范围有限)比传递用户名和密码更好。如果用户名和密码在中间人攻击中泄露,这就像给攻击者城堡的钥匙。

Stormpath的API密钥认证功能就是一个例子。其思想是,你只提供一次硬凭证,然后获得一个令牌来代替硬凭证。

JSON Web Token (JWT)规范正在迅速获得关注。Stormpath强烈推荐它,它提供了结构和安全性,但是可以根据应用程序灵活地修改它。这里有一篇更长的文章:正确使用JWT

在哪里存放你的JWT

所以现在你已经很好地理解了JWT是什么,下一步是找出如何存储这些token。如果你正在构建一个web应用程序,你有两个选择:

  • HTML5 Web存储(localStorage或sessionStorage)
  • cookies

为了比较这两者,假设我们有一个构建好的的AngularJS或单页面应用程序(SPA) galaxies.com,它有一个登录路由(/token)来验证用户,并返回JWT。要访问服务于SPA的其他API端口,客户端需要传递一个有效的JWT。

单页应用程序发出的请求类似于:

HTTP/1.1
 
POST /token
Host: galaxies.com
Content-Type: application/x-www-form-urlencoded
 
username=tom@galaxies.com&password=andromedaisheadingstraightforusomg&grant_type=password

服务器的响应将根据您使用的是cookie还是Web存储而有所不同。为了进行比较,让我们来看看你是如何做到这两点的。

本地存储或sessionStorage (Web存储)

为JWT交换用户名和密码以将其存储在浏览器存储(sessionStorage或localStorage)中是相当简单的。响应体将包含JWT作为访问令牌:

HTTP/1.1 200 OK
 
{
	"access_token": "eyJhbGciOiJIUzI1NiIsI.eyJpc3MiOiJodHRwczotcGxlL.mFrs3Zo8eaSNcxiNfvRh9dqKP4F1cB",
    "expires_in":3600
}

在客户端,你会将令牌存储在HTML5的Web存储中(假设我们有一个成功的回调):

function tokenSuccess(err, response) {
    if(err){
        throw err;
    }
    $window.sessionStorage.accessToken = response.body.access_token;
}

要将访问令牌传递回受保护的api,需要使用HTTP授权头和Bearer scheme。你的SPA会提出类似的要求:

HTTP/1.1
 
GET /stars/pollux
Host: galaxies.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsI.eyJpc3MiOiJodHRwczotcGxlL.mFrs3Zo8eaSNcxiNfvRh9dqKP4F1cB

Cookie存储

为JWT交换用户名和密码以将其存储在cookie中也很简单。响应将使用Set-Cookie HTTP头:

HTTP/1.1 200 OK
 
Set-Cookie: access_token=eyJhbGciOiJIUzI1NiIsI.eyJpc3MiOiJodHRwczotcGxlL.mFrs3Zo8eaSNcxiNfvRh9dqKP4F1cB; Secure; HttpOnly;

要将访问令牌传递回同一域中的API,浏览器将自动包含cookie值。向API发出的请求类似于:

GET /stars/pollux
Host: galaxies.com
 
Cookie: access_token=eyJhbGciOiJIUzI1NiIsI.eyJpc3MiOiJodHRwczotcGxlL.mFrs3Zo8eaSNcxiNfvRh9dqKP4F1cB;

两者区别

如果您对这两种方法进行比较,两者都将接收到一个JWT到浏览器。两者都是无状态的,因为API需要的所有信息都在JWT中。两者都很容易传递回受保护的api。区别在于存储媒介。

sessionStorage and localStorage 安全性

可以通过同一域中的JavaScript访问Web存储(localStorage / sessionStorage)。这意味着在你的站点上运行的任何JavaScript都可以访问web存储,因此很容易受到跨站点脚本攻击 (XSS) 。简单地说,XSS是一种漏洞,攻击者可以在其中注入将在你的页面上运行的JavaScript。XSS攻击者试图通过表单输入注入JavaScript,攻击者将<script>alert('You are Hacked');</script>提交到表单中,看浏览器是否会执行这段脚本并被其它用户看到。

为了防止XSS,通常的应对是转义和编码所有不可信的数据。但这远远不是故事的全部。2015年,现代web应用使用基于CDNs或外部的JavaScript。现代的web应用程序包括用于A/B测试、funnel/market分析和广告。我们使用像Bower这样的包管理器将其他人的代码导入到我们的应用程序中。

如果您使用的脚本中只要有一个被破坏了怎么办?恶意的JavaScript可以嵌入到页面中,Web存储也会受到影响。这些类型的XSS攻击可以在不知情的情况下获取访问您站点的每个人的Web存储。这可能就是为什么许多组织建议不要在web存储中存储任何有价值的东西或任何认证信息的原因,这包括会话标识符和令牌。

作为一种存储机制,Web存储在传输期间不强制执行任何安全标准。无论谁读取Web存储并使用它,都必须确保始终通过HTTPS而不是HTTP发送JWT。

cookie 安全性

当与有HttpOnly标志的cookie一起使用时,不能通过JavaScript访问cookie,并且不受XSS的影响。您还可以设置安全cookie标志,以确保cookie仅通过HTTPS发送。这是在过去使用cookie来存储令牌或会话数据的主要原因之一。现代开发人员不愿使用cookie,因为他们通常需要将session存储在服务器上,从而破坏了RESTful 的最佳实践。如果您在cookie中存储JWT,那么cookie作为一种存储机制不需要将session存储在服务器上。这是因为JWT封装了服务器服务请求所需的一切。

然而,cookie容易受到另一种类型的攻击:跨站点请求伪造(CSRF)。CSRF攻击是一种攻击类型,当恶意web站点、电子邮件或博客导致用户的web浏览器在当前已通过身份验证的受信任站点上执行不需要的操作时,就会发生这种攻击。这利用的是浏览器处理cookie的方式,cookie只能被发送到允许它的域。默认情况下,最初设置cookie的域galaxies.com/,不管你是在galaxies.com还是hahagonnahackyou.com访问最初的域,cookie都会被附带在请求中。

CSRF的工作方式是试图引诱你访问hahagonnahackyou.com。该站点将有一个img标记或JavaScript来模拟发布到galaxies.com的表单,并尝试劫持您的会话(如果session仍然有效)和修改您的帐户。

例如:

<body>
 
  <!– CSRF with an img tag >
 
  <img href="http://galaxies.com/stars/pollux?transferTo=tom@stealingstars.com" />
 
  <!– or with a hidden form post >
 
  <script type="text/javascript">
  $(document).ready(function() {
    window.document.forms[0].submit();
  });
  </script>
 
  <div style="display:none;">
    <form action="http://galaxies.com/stars/pollux" method="POST">
      <input name="transferTo" value="tom@stealingstars.com" />
    <form>
  </div>
</body>

两者都将发送galaxies.com的cookie,并可能导致未经授权的状态更改。可以通过使用同步令牌模式来防止CSRF。这听起来很复杂,但所有现代web框架都支持这一点。

例如,AngularJS有一个解决方案来验证只有您的域才能访问cookie。直接看AngularJS文档:

When performing XHR requests, the $http service reads a token from a cookie (by default, XSRF-TOKEN) and sets it as an HTTP header (X-XSRF-TOKEN). Since only JavaScript that runs on your domain can read the cookie, your server can be assured that the XHR came from JavaScript running on your domain.

翻译

执行XHR请求时,$http服务从cookie(默认情况下为XSRF-TOKEN)中读取一个令牌,并将其设置为http标头(X-XSRF-TOKEN)。因为只有在您的域上运行的JavaScript才能读取cookie,所以您的服务器可以确信XHR来自于在您的域上运行的JavaScript。

你可以通过包含一个xsrfToken JWT声明来使这个CSRF保护无状态:

{
  "iss": "http://galaxies.com",
  "exp": 1300819380,
  "scopes": ["explorer", "solar-harvester", "seller"],
  "sub": "tom@andromeda.com",
  "xsrfToken": "d9b9714c-7ac0-42e0-8696-2dae95dbc33e"
}

如果您正在为AngularJS使用Stormpath SDK,那么无需开发就可以获得无状态的CSRF保护。

利用您的web应用程序框架的CSRF保护使cookie对于存储JWT非常可靠。还可以通过检查来自API的HTTP RefererOrigin 头来部分阻止CSRF。CSRF攻击会有与应用无关的RefererOrigin头。

尽管存储JWT更安全,但cookie可能会给开发人员带来一些麻烦,这取决于应用是否需要跨域访问才能工作。请注意,cookie具有可以修改的附加属性(Domain/Path),以允许您指定允许将cookie发送到何处。使用AJAX,您的服务器端还可以通知浏览器是否应该使用CORS请求发送凭据(包括cookie)。

结论

JWT是一种非常棒的身份验证机制。它们提供了一种结构化的方法来声明用户及其可以访问的内容。可以对它们进行编码和签名以防止在客户端进行篡改,但问题在于细节和存储它们的位置。Stormpath建议您将JWT存储在web应用程序的cookie中,因为它们提供了额外的安全性,并且使用现代web框架可以简化对CSRF的保护。HTML5 Web存储容易受到XSS的攻击,具有更大的攻击表面积,可以影响所有应用程序用户,造成成功攻击。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值