原文地址: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. HDU 1285--确定比赛名次【拓扑排序 &amp;&amp; 邻接表实现】

    确定比赛名次 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Sub ...

  2. Asp.net button防止点击多次数据提交

     <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat= ...

  3. 如何获取Assets的路径

    有两种方法可以获取assets的绝对路径: 第一种方法: String path = file:///android_asset/文件名; 第二种方法: InputStream abpath = ge ...

  4. bzoj1816: [Cqoi2010]扑克牌(二分答案判断)

    1816: [Cqoi2010]扑克牌 题目:传送门 题解: 被一道毒瘤题搞残了...弃了坑来刷刷水题 一开始还想复杂了...结果发现二分水过: 二分答案...然后check一下,joker肯定尽量用 ...

  5. ICMP 隧道——将流量封装进 IMCP 的 ping 数据包中,旨在利用 ping 穿透防火墙的检测

    利用 ICMP 隧道穿透防火墙 转自:http://xiaix.me/li-yong-icmp-sui-dao-chuan-tou-fang-huo-qiang/ 以前穿透防火墙总是使用 SSH 隧道 ...

  6. hdoj--4501--小明系列故事——买年货(三维背包)

    小明系列故事--买年货 Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Tota ...

  7. Effective Java(一)—— 创建和销毁对象

    在客户端(调用端)获取自身实例的方法: 公有的构造器: 类的静态工厂方法: 1. 使用静态工厂方法代替构造器 Boolean 是对基本类型 boolean 的包装类: public final cla ...

  8. RAC连接时的2种方式Connect Time Failver和taf

    1. Client-side Connect Time Failover  在客户端的tnsname中配置多个地址,当用户连接时会按照次序尝试各个地址,直到连接成功,连接好后,不再检测地址是否可用,如 ...

  9. 你务必知道的css简写

    欢迎加入前端交流群来py:749539640   简写属性是可以让你同时设置其他几个 CSS 属性值的 CSS 属性.使用简写属性,Web 开发人员可以编写更简洁.更具可读性的样式表,节省时间和精力. ...

  10. Hibernate 与mybatis的区别

    转自:https://blog.csdn.net/julinfeng/article/details/19821923 为方便以后准备面试,把一些常用的技术整理出来,会不定期更新. 首先简单介绍下两者 ...