UI Development

Web Security: Authentication – Cookies vs Tokens vs HTML5 web storage

There are number of ways to achieve authentication such as cookies, tokens, web storage. Being so many options raises the common question for most of the developers…

 

This article would try to help you to choose the correct option to implement authentication based on your needs.  We are going to cover the basics of Tokens vs. Cookies authentication, token storage in cookies vs. HTML5 web storage (local Storage or session Storage).

 

Cookie vs. Token Authentication:

Before we dive further, let’s first discuss how these two authentication systems work. This diagram provides a simplified overview of the difference between token and cookie approaches to authentication.

 

Cookie-Based Authentication

Cookie-based authentication has been the traditional, tried-and-true method for implementing user authentication.

Traditional cookie-based authentication is stateful. In this approach, session or authentication record must be kept on both sides i.e. server and client-side. It’s known as cookie-based authentication as on the front-end a cookie is created that holds a session identifier, while the server needs to keep track of active sessions in a database. Let us now look at the traditional flow of cookie-based authentication:

  1. User tries to enter using their login credentials.
  2. The server verifies whether the credentials are correct and creates a session which is then stored in a database.
  3. Meanwhile, in the user’s browser, a cookie with the session ID is placed.
  4. On subsequent requests, the session ID stored in the browser is verified against the database and if found valid then the request processed.
  5. And if a user logs out of the app then the session is destroyed both for client-side and server-side.

 

Token-Based Authentication

Authentication with JSON Web Tokens (JWTs) comes first in our mind when we talk about authentication with tokens. Although there are number of ways to implement tokens, JWTs have become the de-facto standard.

Token-based authentication is stateless. This means that the record of which users are logged in or which JWTs have been issued are not kept at server side. Instead, to verify the authenticity of the request, the server uses the token which accompanies every request to the server. Generally, an addition Authorization header in the form of Bearer {JWT} can be used to send the token, but it can also be sent in the body of a POST request or even as a query parameter. Let’s see how this flow works:

  1. User tries to enter using their login credentials.
  2. The correctness of credentials is verified by the server and returns a signed token.
  3. This token is stored client-side, most commonly it’s stored in local storage but can also be stored in session storage or in a cookie as well.
  4. This token is included as an additional Authorization header in subsequent requests to the server. The server decodes the JWT and if the token is valid processes the request.
  5. And if a user logs out of the app then the token is destroyed on client-side, no further interaction with the server is necessary.

 

Since we have got a nice understanding of the basic approach behind the working of token-based authentication and cookie-based authentication, it’s time to do some comparison between them based on a few properties.

 

Stateless, Scalable and Decoupled:

One of the biggest advantages of using tokens over cookies is the fact that token authentication is stateless. Each token contains all the data required to check its validity as well as to convey user information through claims thus making it self-contained. The server is then responsible only to sign tokens on a successful login request and verify that incoming tokens are valid. In fact, the issuing of tokens can also be handled using third-party services such as Auth0 and then the server only needs to verify the validity of the token.

Cross Domain and CORS:

Although cookies work efficiently with singular domains and sub-domains, when it comes to managing cookies across different domains, it can get a little messy. In contrast, a CORS enabled token-based approach makes it trivial to expose APIs to different services and domains. If there is a valid token, requests can be processed, since the JWT is required and checked with every call to the back-end.

Store Data in the JWT:

In cookie-based approach, you simply store the session id in a cookie. On the other hand, JWT’s gives you the freedom to store any type of metadata if it’s a valid JSON. There are different types of claims that can be included in the JWT, such as reserved, public and private.

This means that, in practice, a JWT can contain any type of data. You may choose to make the minimum number of claims such as the user id and expiration of the token or based on your use case you may also include additional claims such as scopes or permissions for the user, who issued the token, his email address, and more.

Performance:

The back-end has to do a lookup when using the cookie-based authentication, and the round trip is likely to take longer compared to decoding a token. While you can save yourself additional lookup calls to get and process the requested data in case of token-based approach since you can store additional data inside the JWT, such as the user’s permission level.

For example, let’s assume you had an API resource /myapi/orderlist that retrieves the latest orders placed via your app, but only users with admin as role have access to view this data. In a cookie-based approach, you have to make one request to the database to verify that the session is valid, another to get the user data and to verify that the user is admin, and finally, a third call to retrieve the data. On the other hand, with a JWT approach, you can store the role of the user in the JWT, so once the request is made and the JWT verified, you can retrieve the orders with a single call to the database.

Mobile Ready:

Based on experiences, it’s found that native mobile platforms and cookies do not mix well. While possible, there are many considerations and limitations in using cookies with mobile platforms. Tokens, on the other hand, are much simpler to implement on both Android and iOS. Tokens are also easier to implement for applications and services that do not have a concept of a cookie store.

Size:

Size of JWTs can be the biggest disadvantage of token authentication. A session cookie is relatively small compared to even the tiniest JWT. If you add many claims to JWT depending on your use case, the size of the token could become problematic. Remember, JWT must be included in each request to the server.

 

Where to Store Your JWTs

 

So now that you have a good understanding about the basic difference between JWT and cookies, so now we have to figure out how to store these tokens. While building a web application, we have a couple of options:

  • HTML5 Web Storage (local storage or session storage)
  • Cookies

 

To compare these two, let’s take an example of an AngularJS or single page app (SPA) called orderCheckout.com with a login route as /validToken to authenticate users to return a JWT. And the client needs to pass a valid JWT to access the other APIs endpoints that serve your SPA.

The request that the SPA makes would be like:

HTTP/1.1
POST /validToken
Host: orderCheckout.com
Content-Type: application/x-www-form-urlencoded
username=ank@orderCheckout.com&password=admin123&grant_type=password

Based on whether you are using cookies or Web Storage, your server’s response can vary. For comparison, let’s look at how it works in both.

JWT local storage or session storage (Web Storage)

It’s easy to exchange a username and password for a JWT to store it in browser storage. The response body would contain the JWT as an access token:

HTTP/1.1 200 OK 
  {
  "access_token": “eyJhbGciOiJIUIsI.eyJpc3MiOiJoHRwczGxlL.mFrs3Zo8eaSNcxiNh9dqP4F1cB",
       "expires_in":5600
   }

If we have a success callback, we have to store the token in HTML5 Web Storage on the client side.

function onTokenReceive (err, response) {
    if(error) {
        throw error;
    }
    $window.sessionStorage.myToken = response.body.access_token;
}

We would use the HTTP Authorization Header and the Bearer scheme to pass the access token back to our protected APIs. The request that your SPA makes would resemble:

HTTP/1.1
GET /orders/price
Host: orderCheckout.com
Authorization: Bearer eyJhbGciOiJIUIsI.eyJpc3MiOiJoHRwczGxlL.mFrs3Zo8eaSNcxiNh9dqP4F1cB

 

JWT Cookie Storage

Exchanging a username and password for a JWT to store it in a cookie is easy as well. Set-Cookie HTTP header would be used by the response:

HTTP/1.1 200 OK
 Set-Cookie: access_token=eyJhbGciOiJIUIsI.eyJpc3MiOiJoHRwczGxlL.mFrs3Zo8eaSNcxiNh9dqP4F1cB; Secure; HttpOnly;

The browser would automatically include the cookie value to pass the access token back to protected APIs on the same domain. The request to protected API would be like:

GET /orders/price
Host: orderCheckout.com
Cookie: access_token= eyJhbGciOiJIUIsI.eyJpc3MiiJoHRwczGxlL.mFrs3Zo8eaSNcxiNh9dqP4F1cB;

 

JWT web storage Security vs JWT Cookie Storage Security

JWT session storage and local storage Security:

JavaScript can easily access web Storage (local storage/session storage) on the same domain. This means that it can be vulnerable to cross-site scripting (XSS) attacks. XSS is a type of vulnerability where an attacker can inject JavaScript that can run on your page and result into unwanted behavior.

The common way is to escape and encode all untrusted data to prevent XSS. But what if malicious JavaScript gets embedded on the page, and Web Storage is compromised. These types of XSS attacks can get control over everyone’s Web Storage that visits your site, without their knowledge. This can probably be one of the reasons why a bunch of organizations advises not to store anything valuable or trust any information in web storage. This includes both session identifiers and tokens.

Web Storage as a storage mechanism does not enforce any security standards during data transfer. It is advised that whoever reads and uses Web Storage must do their due diligence to ensure that they never send the JWT over HTTP instead should always use HTTPS.

JWT Cookie Storage Security

When we use cookies with the HttpOnly cookie flag, they are not accessible through JavaScript as well as immune to XSS. To guarantee that cookie is only sent over HTTPS, you can also set the Secure cookie flag. This is one of the key reasons why cookies have been leveraged in the past to store session data or tokens. Modern developers normally hesitate to use cookies as they traditionally required a state to be stored on the server, thus breaking RESTful best practices. But if you are storing a JWT in the cookie then cookies as a storage mechanism do not require a state to be stored on the server. The reason behind that is JWT encapsulates everything the server needs to serve the request.

However, cookies are vulnerable to another type of attack known as cross-site request forgery (CSRF). A CSRF is a type of attack that occurs when a malicious email, web site, 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 kind of attacks exploits the way a browser handles cookie. As we know cookie can only be sent to the allowed domains. The domain that originally set the cookie is by default domain. The cookie will be sent for a request regardless of whether you are on orderCheckout.com or iamgonnahackyou.com

CSRF works by attempting to induce you to iamgonnahackyou.com. That site will have either some JavaScript or an img tag to imitate a form post to orderCheckout.com and attempt to hijack your session and modify your account.

We can use synchronized token patterns to prevent CSFR, currently, all modern web frameworks have support for this. For example, AngularJS provides a solution to validate that the cookie is accessible by only your domain.

During XHR requests, the $http service reads a token from a cookie which is by default a XSRF-TOKEN and sets it as an HTTP header: X-XSRF-TOKEN. Since the JavaScript that runs on your domain can only read the cookie, your server will be assured that the XHR is coming from JavaScript running on your domain.

Leveraging CSRF protection in your web app framework’s makes cookies mechanism rock solid for storing a JWT.

Conclusion

In this article we understood and compared the cookie- and token-based authentication based on some criterias. We highlighted the advantages and concerns of using tokens. JWT gives you a structured way to declare users and their accessibility but the devil is in the details and where you store them. Based on reading we recommend you to store your JWT in cookies for web applications, as it provides additional security, the simplicity of protecting against CSRF with modern web frameworks. HTML5 Web Storage has a larger attack surface area, also vulnerable to XSS, and on a successful attack can impact all application users.

Thank You…!

About The Author