Storing your JWTs in HTML Web Storage is (maybe) a bad idea

Liam Wang (Ex-Uber)
6 min readJul 7, 2018

--

In this post, I will tell you why storing your JWTs in HTML web storage (session storage and local storage) can open up some vulnerabilities for your web app if you are doing it wrong.

Since this will be a long post with lots of information on it, I will provide the table contents here

  • What is HTML web storage?
  • What is JWTs (Json Web Tokens)?
  • Why Storing JWTs in HTML web storage is (maybe) a bad idea?
  • Best Practice of storing JWTs in HTML web storage?
  • Alternative of storing JWTs (other than web storage)?
  • What is the drawback of storing JWT’s in cookies?
  • Best Practice of storing JWTs in cookies?
  • Conclusion

What is HTML web storage?

HTML web storage (known as HTML5 storage) is a place where web applications can store its data locally.

There are 2 types of HTML5 storage:

  • Session Storage -> data will only available for the duration of browser session and will be deleted once the tab or window is closed.
  • Local Storage -> data will not be gone even after the tab or window is closed. However, the user can delete the local storage manually.

PS: Data persisted in the HTML web storage should be non-sensitive data because can easily be read by the hacker.

Web storage access is usually per origin which means it can only be accessed on the same domain and protocol.

Example:

  1. Different protocol (http and https) -> Data in the http://mywebsite.com web storage cannot be accessed by Javascript running on https://mywebsite.com
  2. Different subdomains -> Data in the http://mywebsite.com cannot be accessed by Javascript running on http://sub.mywebsite.com

What is JWTs (Json Web Tokens)?

JWTs is the most popular authentication mechanism. It is useful standard because it sends information that can be verified and trusted with a digital signature. The idea is that you can signed your JWTs (called claims) and can be verified later with the secret key.

Some of the benefits are

  1. JWTs is a stateless authentication mechanism.
  2. JWTs can be cryptographically signed and encrypted to prevent tampering on client side.
  3. JWTs can store whatever you need and you will use JSON to handle it.

Format of JWTs

The compacted representation of a signed JWT is a string that has three parts, each separated by a .:

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2UifQ.ipevRNuRP6HflG8cFKnmUPtypruRC4fb1DWtoLL62SY

This is a Base64 encoded string and consists of three sections separated by a .

(1) eyJhbGciOiJIUzI1NiJ9
.
(2) eyJzdWIiOiJKb2UifQ
.
(3) ipevRNuRP6HflG8cFKnmUPtypruRC4fb1DWtoLL62SY

(1) Header: contains metadata of tokens, type of signature and encryption algorithm.

(2) Claims: contains information(payload) that you want to sign.

(3) JWS(JSON WEB SIGNATURE): algorithm to sign both header and claims.

We can decode the second section (2), the payload, to get this nice JSON object:

{"iss": "http://galaxies.com","exp": 1300819380,"scopes": ["explorer", "solar-harvester", "seller"],"sub": "tom@andromeda.com"}
  • iss = issuer -> who issued the token
  • exp = expiration -> when the token expires?
  • scopes = scope -> what can the user access with this token?
  • sub = subject -> who is this person?

Tokens is usually given to the users after they enter login, typically using email/username and password, but instead of login, users can provide some API keys or tokens from another service.

It is however not safe to store sensitive data in JWT as JWT only verify by digital signature to protect against manipulation and not encrypted. However, there is JWE (JSON Web Encryption) if you do need to store sensitive information. The JWTs article itself is pretty long by now and contains too much information, so I will discuss JWE in the other article next time.

Why Storing JWTs in HTML web storage is (maybe) a bad idea?

So, by now you probably have an idea what does the title refer as the bad idea. In the next section, I will discuss what is the alternative of HTML storage, and what is the weakness. But to provide some hints, both of them can be either a good or bad idea depending on what are you comfortable with and what are your needs.

After going through what is HTML5 storage and what is JWT’s you probably realize that we have a big responsibility to not store sensitive information on both JWTs and HTML web storage.

If you remember in the HTML5 Storage section, web storage can be accessed by JavaScript on the same domain and protocol. It means that someone can potentially use JavaScript to access your web storage by doing some cross-site scripting(XSS) attacks. When the hacker doing XSS attacks, what they do essentially is injecting JavaScript to your web app usually through some kind of form inputs. One way to prevent XSS, is to escape and sanitize all input data to make sure its safe.

You will probably tell me that is a no brainer thing to do because the framework did that for you or you have sanitized every form inputs on your website and make sure that nobody can inject JavaScript. The truth is you have done the good job, but there are many ways hacker try to get into your web app.

One way, they can access outside infrastructure or 3rd party JavaScript libraries you use which might have some xss vulnerabilities. In the bigger projects, you probably have tons of 3rd party JS library and the hacker only need to scan one of those to embed some JS scripts on your web app to accessed your web storage.

Best Practice of storing JWTs in HTML web storage?

Because web storage does not enforce any security standards, the best practice of storing JWTs in web storage are:

  1. Always avoid storing sensitive data on your JWTs.
  2. Always send JWTs over HTTPS instead of HTTP.

Alternative of storing JWTs (other than web storage)?

Now you might be wondering, if storing in web storage is not that safe, is there any other places I can store my JWTs?

The answer is YES and it is COOKIES.

Unlike web storage, cookies can be access with a different subdomain or different protocols as well.

Example: A cookie with a domain mywebsite.com will be sent to both http://www.mywebsite.com and https://www.mywebsite.com

Example: A cookie with a domain mywebsite.com will be sent to sub.mywebsite.com as well.

There is a way to override this by having the secure attribute, which will only be sent to https.

There is also another attribute called httpOnly that will disable JavaScript from reading the cookies, this of course can prevent the XSS attack.

The response would use Set-Cookie HTTP header:

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

What is the drawback of storing JWT’s in cookies?

First of all, cookies can have a smaller capacity to store data which is 4Kb compare to web storage which is up to10MB.

Cookies might be immune to XSS attack with the “httpOnly” flag, but cookies introduce another vulnerabilities such as CSRF which stands for Cross Site Request Forgery.

In a very simple way, CSRF will bring you to the malware sites (evilcsrf.com) and on the site, they will have images which emulates the form post in the banking site. It can hijack your session and perform some unwanted action such as transferring money or deleting your account using your session.

Best Practice of storing JWTs in cookies?

Many modern frameworks actually have their own solution to prevent CSRF attack. It is usually pretty easy to set up this CSRF protection.

Another way to prevent the CSRF attack is to check the HTTP Referer and Origin header from your API. To detect the CSRF attack, check out those values, and HTTP Referer and Origin will usually contains unrelated value to your application.

Conclusion

If you have to take away one lessons from this very long post, it will be never store any sensitive data on JWTs and web storage. Web storage will be vulnerable to XSS attack, and can potentially lead to stealing some sensitive data of your users. While cookies will be vulnerable to CSRF attack but many modern framework have implemented their own CSRF protection and it is relatively easy to set that up. So it is up to you where you want to store your needs, if your JWTs is more than 4KB then you the only options is probably web storage. Best practice is always remember to store any non sensitive data and always encrypt your data also send it over https. Let me know your personal thought about this.

--

--

Liam Wang (Ex-Uber)

Passionate about software engineer, react, and python. Ex-Software Engineer @uber, twitter: @liamwangcode