原文地址:https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage

Update 5/12/2016: Building a Java application? JJWT is a Java library providing end-to-end JWT creation and verification, developed by our very own Les Hazlewood. Forever free and open-source (Apache License, Version 2.0), JJWT is simple to use and understand. It was designed with a builder-focused fluent interface hiding most of its complexity. We’d love to have you try it out, and let us know what you think! (And, if you’re a Node developer, check out NJWT!)

Stormpath has recently worked on token authentication features using JSON Web Tokens (JWT), and we have had many conversations about the security of these tokens and where to store them.

If you are curious about your options, this post is for you. We will cover the basics of JSON Web Tokens (JWT) vs. OAuth, token storage in cookies vs. HTML5 web storage (localStorage or sessionStorage), and basic security information about cross-site scripting (XSS) and cross-site request forgery (CSRF).

Let’s get started…

JSON Web Tokens (JWT): A Crash Course

The most implemented solutions for API authentication and authorization are the OAuth 2.0 and JWT specifications, which are fairly dense. Cliff’s Notes Time! Here’s what you need to know about JWT vs OAuth:

  • JWTs are a great authentication mechanism. They give you a structured and stateless way to declare a user and what they can access. They can be cryptographically signed and encrypted to prevent tampering on the client side.
  • JWTs are a great way to declare information about the token and authentication. You have a ton of freedom to decide what makes sense for your application because you are working with JSON.
  • The concept behind scopes is powerful yet incredibly simple: you have the freedom to design your own access control language because, again, you are working with JSON.

If you encounter a token in the wild, it looks like this:

 
1
2
"dBjftJeZ4CVP.mB92K27uhbUJU1p1r.wW1gFWFOEjXk…"

This is a Base64 encoded string. If you break it apart you’ll actually find three separate sections:

 
1
2
3
4
5
6
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
.
eyJpc3MiOiJodHRwOi8vZ2FsYXhpZXMuY29tIiwiZXhwIjoxMzAwODE5MzgwLCJzY29wZXMiOlsiZXhwbG9yZXIiLCJzb2xhci1oYXJ2ZXN0ZXIiXSwic3ViIjoic3RhbmxleUBhbmRyb21lZGEuY29tIn0
.
edK9cpfKKlGtT6BqaVy4bHnk5QUsbnbYCWjBEE7wcuY

The first section is a header that describes the token. The second section is a payload which contains the juicy bits, and the third section is a signature hash that can be used to verify the integrity of the token (if you have the secret key that was used to sign it).

When we magically decode the second section, the payload, we get this nice JSON object:

 
1
2
3
4
5
6
7
{
  "iss": "http://galaxies.com",
  "exp": 1300819380,
  "scopes": ["explorer", "solar-harvester", "seller"],
  "sub": "tom@andromeda.com"
}

This is the payload of your token. It allows you to know the following:

  • Who this person is (sub, short for subject)
  • What this person can access with this token (scope)
  • When the token expires (exp)
  • Who issued the token (iss, short for issuer)

These declarations are called ‘claims’ because the token creator claims a set of assertions that can be used to ‘know’ things about the subject. Because the token is signed with a secret key, you can verify its signature and implicitly trust what is claimed.

Tokens are given to your users after they present some credentials, typically a username and password, but they can also provide API keys, or even tokens from another service. This is important because it is better to pass a token (that can expire, and have limited scope) to your API than a username and password. If the username and password are compromised in a man-in-the-middle attack, it is like giving an attacker keys to the castle.

Stormpath’s API Key Authentication Feature is an example of this. The idea is that you present your hard credentials once, and then get a token to use in place of the hard credentials.

The JSON Web Token (JWT) specification is quickly gaining traction. Recommended highly by Stormpath, it provides structure and security, but with the flexibility to modify it for your application. Here is a longer article on it: Use JWT the Right Way!

Where to Store Your JWTs

So now that you have a good understanding of what a JWT is, the next step is to figure out how to store these tokens. If you are building a web application, you have a couple of options:

  • HTML5 Web Storage (localStorage or sessionStorage)
  • Cookies

To compare these two, let’s say we have a fictitious AngularJS or single page app (SPA) called galaxies.com with a login route (/token) to authenticate users to return a JWT. To access the other APIs endpoints that serve your SPA, the client needs to pass a valid JWT.

The request that the single page app makes would resemble:

 
1
2
3
4
5
6
7
8
HTTP/1.1
 
POST /token
Host: galaxies.com
Content-Type: application/x-www-form-urlencoded
 
username=tom@galaxies.com&password=andromedaisheadingstraightforusomg&grant_type=password

Your server’s response will vary based on whether you are using cookies or Web Storage. For comparison, let’s take a look at how you would do both.

JWT localStorage or sessionStorage (Web Storage)

Exchanging a username and password for a JWT to store it in browser storage (sessionStorage or localStorage) is rather simple. The response body would contain the JWT as an access token:

 
1
2
3
4
5
6
7
HTTP/1.1 200 OK
 
  {
  "access_token": "eyJhbGciOiJIUzI1NiIsI.eyJpc3MiOiJodHRwczotcGxlL.mFrs3Zo8eaSNcxiNfvRh9dqKP4F1cB",
       "expires_in":3600
   }

On the client side, you would store the token in HTML5 Web Storage (assuming that we have a success callback):

 
1
2
3
4
5
6
7
function tokenSuccess(err, response) {
    if(err){
        throw err;
    }
    $window.sessionStorage.accessToken = response.body.access_token;
}

To pass the access token back to your protected APIs, you would use the HTTP Authorization Header and the Bearer scheme. The request that your SPA would make would resemble:

 
1
2
3
4
5
6
HTTP/1.1
 
GET /stars/pollux
Host: galaxies.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsI.eyJpc3MiOiJodHRwczotcGxlL.mFrs3Zo8eaSNcxiNfvRh9dqKP4F1cB

JWT Cookie Storage

Exchanging a username and password for a JWT to store it in a cookie is simple as well. The response would use the Set-Cookie HTTP header:

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

To pass the access token back to your protected APIs on the same domain, the browser would automatically include the cookie value. The request to your protected API would resemble:

 
1
2
3
4
5
GET /stars/pollux
Host: galaxies.com
 
Cookie: access_token=eyJhbGciOiJIUzI1NiIsI.eyJpc3MiOiJodHRwczotcGxlL.mFrs3Zo8eaSNcxiNfvRh9dqKP4F1cB;

So, What’s the difference?

If you compare these approaches, both receive a JWT down to the browser. Both are stateless because all the information your API needs is in the JWT. Both are simple to pass back up to your protected APIs. The difference is in the medium.

JWT sessionStorage and localStorage Security

Web Storage (localStorage/sessionStorage) is accessible through JavaScript on the same domain. This means that any JavaScript running on your site will have access to web storage, and because of this can be vulnerable to cross-site scripting (XSS) attacks. XSS, in a nutshell, is a type of vulnerability where an attacker can inject JavaScript that will run on your page. Basic XSS attacks attempt to inject JavaScript through form inputs, where the attacker puts <script>alert('You are Hacked');</script>into a form to see if it is run by the browser and can be viewed by other users.

To prevent XSS, the common response is to escape and encode all untrusted data. But this is far from the full story. In 2015, modern web apps use JavaScript hosted on CDNs or outside infrastructure. Modern web apps include 3rd party JavaScript libraries for A/B testing, funnel/market analysis, and ads. We use package managers like Bower to import other peoples’ code into our apps.

What if only one of the scripts you use is compromised? Malicious JavaScript can be embedded on the page, and Web Storage is compromised. These types of XSS attacks can get everyone’s Web Storage that visits your site, without their knowledge. This is probably why a bunch of organizations advise not to store anything of value or trust any information in web storage. This includes session identifiers and tokens.

As a storage mechanism, Web Storage does not enforce any secure standards during transfer. Whoever reads Web Storage and uses it must do their due diligence to ensure they always send the JWT over HTTPS and never HTTP.

JWT Cookie Storage Security

Cookies, when used with the HttpOnly cookie flag, are not accessible through JavaScript, and are immune to XSS. You can also set the Secure cookie flag to guarantee the cookie is only sent over HTTPS. This is one of the main reasons that cookies have been leveraged in the past to store tokens or session data. Modern developers are hesitant to use cookies because they traditionally required state to be stored on the server, thus breaking RESTful best practices. Cookies as a storage mechanism do not require state to be stored on the server if you are storing a JWT in the cookie. This is because the JWT encapsulates everything the server needs to serve the request.

However, cookies are vulnerable to a different type of attack: cross-site request forgery (CSRF). A CSRF attack is a type of attack that occurs when a malicious web site, email, or blog causes a user’s web browser to perform an unwanted action on a trusted site on which the user is currently authenticated. This is an exploit of how the browser handles cookies. A cookie can only be sent to the domains in which it is allowed. By default, this is the domain that originally set the cookie. The cookie will be sent for a request regardless of whether you are on galaxies.com or hahagonnahackyou.com.

CSRF works by attempting to lure you to hahagonnahackyou.com. That site will have either an img tag or JavaScript to emulate a form post to galaxies.com and attempt to hijack your session, if it is still valid, and modify your account.

For example:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<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>

Both would send the cookie for galaxies.com and could potentially cause an unauthorized state change. CSRF can be prevented by using synchronized token patterns. This sounds complicated, but all modern web frameworks have support for this.

For example, AngularJS has a solution to validate that the cookie is accessible by only your domain. Straight from AngularJS docs:

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.

You can make this CSRF protection stateless by including a xsrfToken JWT claim:

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

If you are using the Stormpath SDK for AngularJS, you get stateless CSRF protection with no development effort.

Leveraging your web app framework’s CSRF protection makes cookies rock solid for storing a JWT. CSRF can also be partially prevented by checking the HTTP Referer and Origin header from your API. CSRF attacks will have Referer and Origin headers that are unrelated to your application.

Even though they are more secure to store your JWT, cookies can cause some developer headaches, depending on if your applications require cross-domain access to work. Just be aware that cookies have additional properties (Domain/Path) that can be modified to allow you to specify where the cookie is allowed to be sent. Using AJAX, your server side can also notify browsers whether credentials (including Cookies) should be sent with requests with CORS.

Conclusion

JWTs are an awesome authentication mechanism. They give you a structured way to declare users and what they can access. They can be encrypted and signed for to prevent tampering on the client side, but the devil is in the details and where you store them. Stormpath recommends that you store your JWT in cookies for web applications, because of the additional security they provide, and the simplicity of protecting against CSRF with modern web frameworks. HTML5 Web Storage is vulnerable to XSS, has a larger attack surface area, and can impact all application users on a successful attack.

Where to Store your JWTs – Cookies vs HTML5 Web Storage--转的更多相关文章

  1. HTML5 Web Storage

    Web Storage是HTML5 API提供一个新的重要的特性: 最新的Web Storage草案中提到,在web客户端可用html5 API,以Key-Value形式来进行数据持久存储: 目前主要 ...

  2. HTML5 Web Storage使用实例

    很久没写文章了,忙加懒实在没办法,之前也看过关于Web Storage的文章,当时就觉得各各浏览器的支持跟上来还早着呢,像我们这样做门户网站的一时半会儿也用不上,毕竟用户群体鱼目混杂嘛,最近各各浏览器 ...

  3. HTML5 Web Storage 特性

    原文地址: Using HTML5 Web Storage 原文日期: 2010年06月28日 翻译日期: 2013年08月12日 当下Web开发领域最火爆的词语当属 HTML5.HTML5标准的新特 ...

  4. HTML5 Web Storage -- 让Cookies看起来如此古老

    转载 原文 在此两部分组成的系列中,我们将来看看HTML5 规范中最棒而且最有趣的特性之一的Web Storage.我们将看看Web Storage 和 Cookies的历史,并从考虑以下几点: *C ...

  5. HTML5 Web Storage概述

    Web Storage html5新增功能 可以在客户端本地保存数据 之前是使用Cookies在客户端保存注入用户名等简单用户信息,但永久数据存在几个问题 大小:cookies大小被限制在4KB 带宽 ...

  6. html5 Web Storage(localStorage(),sessionStorage())

    Web Storage包括了两种存储方式:sessionStorage和localStorage sessionStorage 是会话级别的存储,这些数据只有在同一个会话中的页面才能访问并且当会话结束 ...

  7. HTML5 WEB Storage - localStorage存储位置在哪

    localStorage作为客户端浏览器持久化存储方案 这个是浏览器隔离的,每个浏览器都会把localStorage存储在自己的UserData中,如chrome一般就是 C:\Users\你的计算机 ...

  8. web 存储方式汇总:Cookies,Session, Web SQL; Web Storage(LocalStorage ,SessionStorage),IndexedDB,Application Cache,Cache Storage

    1 1 1 web 存储方式汇总: 旧的方式: Cookies; Session; Web SQL; 新的方式 HTML5 : Web Storage(LocalStorage ,SessionSto ...

  9. HTML5本地存储 Web Storage

    Web Storage基本介绍 HTML5 定义了本地存储规范 Web Storage , 提供了两种存储类型 API  sessionStorage 和 localStorage,二者的差异主要是数 ...

随机推荐

  1. cocos2d-x 3.0的坑有哪些

    问题一:setup.py 之后, ANT文件夹为什么创建不成功? ANT文件夹要指定到bin以下,NDK和SDK则指定要根文件夹就可以 问题二:cocos run -p android 之后,执行应用 ...

  2. bzoj2748: [HAOI2012]音量调节(背包)

    2748: [HAOI2012]音量调节 题目:传送门 题解: sb省选题..呵呵一眼背包: f[i][j]表示第i时刻能否为音量j 代码: #include<cstdio> #inclu ...

  3. javascript系列-class9.DOM(上)

    欢迎加入前端交流群交流知识获取视频资料:749539640 1.文档对象模型DOM(document Object Model)        所谓DOM就是以家族的形式描述HTML       节点 ...

  4. js中cookie的使用 以及缺点

      什么是Cookie Cookie意为“甜饼”,是由W3C组织提出,最早由Netscape社区发展的一种机制.目前Cookie已经成为标准,所有的主流浏览器如IE.Netscape.Firefox. ...

  5. 配置 NTP 时间服务器

    对于我们当前这种案例,主要目标是把 z01 这台服务器设置为时间服务器,剩下的 z02,z03 这两台机器同步 z01 的时间,我们需要这样做的原因是因为,整个集群架构中的时间,要保持一致. ** 检 ...

  6. 51nod 1307 绳子与重物 (标记父节点更新即可)

    1307 绳子与重物 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 有N条绳子编号 0 至 N - 1,每条绳子后面栓了一个重物重量为Wi,绳子的最大负重为Ci. ...

  7. Mysql数据库批量添加数据

    1.目的: 用于向Mysql表中批量插入数据 2.过程: 3.语句 #删除已有存储过程 DROP PROCEDURE if exists insertdata; #设定命令行出现//后再执行语句 de ...

  8. 【原创】如何使用一句SQL计算工作日天数?

    现在有这样一个需求,要求计算两个日期间的工作日天数,要求除去节假日,其中节假日有一张配置表,具体的格式如下: 开始日期 结束日期 节假日类型 节假日名称 2013-08-10 2013-08-12   ...

  9. angular.js学习-ng-grid

    ng-grid是基于AngularJS和JQuery的富表格控件,由AngularUI Team领衔开发,到目前为止已有2354次Commit,1076个Fork.  AngualrUI:http:/ ...

  10. SpringCloud学习笔记(12)----Spring Cloud Netflix之Hystrix断路器的流程和原理

    工作流程(参考:https://github.com/Netflix/Hystrix/wiki/How-it-Works) 1. 创建一个HystrixCommand或HystrixObservabl ...