Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

saving authentication token in cookie (Django Rest Framework + React)

So, as the title says I am using Django Rest Framework, combined with React.

I authenticate users using token authentication. Now I am facing a problem. When I reload a page (by pressing F5 key, for instance) all the state is gone, and I can't save the token in such cases, requiring user to sign in once more.

I thought about storing the token in a cookie, but that doesn't seem very safe.

There are other questions like this, but no answer really explains how much of a security risk this is. I figure it is quite high, since having the token seems to be enough to authenticate as someone to the back-end.

So, my question is: Is my assumption that it is not safe to store my authentication token in a cookie true?

Note: I am thinking about switching to session based authentication, but I'd rather safe me the work and keep the token authentication.

like image 569
Rik Schoonbeek Avatar asked Jul 02 '18 20:07

Rik Schoonbeek


People also ask

How do you get auth tokens in Django?

Request an Auth Token in Django REST FrameworkThe Django REST Framework will provide an endpoint so that the user can request a Token for authentication with their password and username. It won't handle GET requests. It will inform you to use POST request with username and password.

What is JWT authentication in Django REST framework?

JSON Web Token Authentication Unlike the built-in TokenAuthentication scheme, JWT Authentication doesn't need to use a database to validate a token. A package for JWT authentication is djangorestframework-simplejwt which provides some features as well as a pluggable token blacklist app.

How JWT token works in Django REST framework?

After verifying the credentials, the server issues two JSON Web Tokens to the user. One of them is an Access Token and the other is a Refresh Token. The frontend of your application then stores the tokens securely and sends the Access Token in the Authorization header of all requests it then sends to the server.

Which authentication is best in Django REST framework?

JSON Web Token (JWT) Authentication This is a new and popular standard that works similar to TokenAuthentication except that it does not need to save tokens in the database.


1 Answers

I forgot my old answer was still up on the internet getting some traction... and it's partly wrong, as I realized later.

Updated Answer:

I thought about storing the token in a cookie, but that doesn't seem very safe.

It's safe enough, if done correctly. Yes, the cookie will still be readable by anyone having physical access to the system. More on this below.

Let's make one thing clear, It is absolutely necessary to store some data on client-side (browser in this case), and send this data with API calls to authenticate users. Let's call this data "token".

When you send this token in API calls, anyone having physical access to the system can view it. Also, there's no point of encrypting it because...

  1. You'll need to decrypt it before sending, which makes the unencrypted string readable.
  2. You'll need to store the decryption key on client-side as well, which brings us back to the same problem.

What are the options? Honestly, let it be. Most websites work like this (almost). Unless the website holds some very sensitive information. In that case, look into time-based passwords, hardware-based passwords, bio-metrics maybe? These methods mostly passes on the responsibility of safely keeping the keys to user.

You can of course, make it more secure. Here are some tips:

  1. Remove token from server after a certain period of time/inactivity.
  2. Update token on random requests, and invalidate previous ones.
  3. Allow users to view active sessions, and removing them.
  4. Bind it to user IP, or something hard-to-replicate. Every time user logs in with different IP, ask for password.

With that in mind, let's talk about storing the token.

We just need to make sure other websites, malicious scripts, and software cannot access the stored token. (The user can always read the token, as mentioned above.)

You can either store it in cookies or localStorage. Both work fine, but localStore is designed to store larger data. Cookies can store upto 4096 bytes of data - which is enough for storing token. Cookies also help when working on SSR (server-side rendering). Though, it can get tricky to handle cookies in React. (Tip: Try next.js, it has built-in support for cookies and SSR with React.)

You can also specify expiration time in cookies, if that helps.

TL;DR: Using Cookies is perfectly fine. Just use it properly.

Thanks to @ShayanSalehian for pointing this out: LocalStorage is subject to XSS and cookie is subject to CSRF. So I think using cookies + CSRF is the most secure way even in TokenAuthentication for storing tokens on client...


Old Answer (Spoiler alert, it's partly wrong.):

This is something I had to deal with as well. Lost a few nights' sleep in the process.

Disclaimer: I'm not any expert in security. Just a bit obsessed (read: paranoid).

Short version (to answer your question): I finally ended up using window.localStorage to store the token. Though I'm not convinced myself that it's the best thing to do, but it's not just about the "storing" part - read long version to understand more.

Long version: First, let's clarify a few things. React is more like a mobile app than a webpage/website. I'm not talking about React Native - I mean React.js.

Why am I saying it's more like a mobile app than a website? Traditional websites usually use session based authentication, for which browsers/servers are typically prepared. It's a no brainer and seamless task apparently.

In a mobile app (or client-side standalone app), you need to maintain some sort of token to essentially tell the server "Hey, it's me! I visited a while ago. Here's my identity card. Let me in, please?". The problem is, it's hard to keep the token secure at client end. Android itself didn't provide any secure way to store authentication token until Android v4.3. That too wasn't secure enough, so they introduced hardware-backed keystore a while ago. This is the reason why some apps didn't (and still don't) work with rooted devices. Read more about this here: https://stackoverflow.com/a/19669719/3341737

Compared to a React/standalone web app, Google (somewhat) controls Android client-end. It's comparatively easier for them to implement hardware-based keystore. In case of web app, there are tons of browsers, with hundreds of versions and whatnot.

Coming back to window.localStorage. Similar to Cookies, localStorage is isolated for each domain. Since it's a newer API, it's designed in a better way than good old Cookies.

There's no point in encrypting the key (you may obfuscate it though), as you'll need to store the decryption key somewhere locally as well. So if someone can get access to the token, they can access decryption key as well.

Second aspect of this issue (and why "storing" isn't the only problem) is - from whom do you really want to protect the token?

  1. Man in the middle? Use SSL.
  2. Other websites? They can't access localStorage of your domain.
  3. Some person? True. They can get token easily if they have physical access to the PC. Having physical access practically makes them the user (you want to protect the token from the user?). Considering that the person has physical access, you cannot protect the token even if you somehow securely store it.

Why not? Because you'll need to send the token with each request - and data sent with every request is available in browser network inspector. So regardless where and how you store the token, it can be stolen by someone with physical access to PC.

Why not Cookie? Two reasons (1 actually):

  1. Cookie is sent with every request by default. You really don't need this because you only need to send token during API calls (not page loads). Also, since you're using DRF (applicable to any RESTful API backend) you probably need to send token is a specific way to server - which require custom method any way.
  2. window.localStorage is a neat little API to work with (just window.localStorage.setItem('key', 'value')). Also, the maximum size restriction is much higher as compared to Cookies.

Thus, window.localStorage seems a feasible option to me. Enlighten me if you have a better solution.

That being said, this does not mean you can't improve the security. Here are a few suggestions:

  1. Invalidate/delete the token from DB after a certain period of time or after a certain period of inactivity.
  2. Send updated token (also invalidate previous one) with requests randomly enough, and replace stored token with new one.
  3. Give user access to delete their account's saved tokens along with last activity of each token.
  4. If you're really (really) concerned, bind token with user IP (or something else which can't be cloned to another system). If user logs in from a new device/location/browser use 2FA.
like image 167
TheKalpit Avatar answered Sep 29 '22 18:09

TheKalpit