DEV Community

loading...
Cover image for Secure JSON Web Token with Unique Browser IDs

Secure JSON Web Token with Unique Browser IDs

alexanderschau profile image Alexander Schau ・2 min read

JSON web token (JWT) are a really good opportunity to create secure login workflows without storing tons of access tokens in your database. But as good as it might sound in the first moment they have a huge problem. After giving them to a user, you can't really control, if someone will copy them to another machine.

This makes it easy for everyone to just copy a JSON web token out of the Local Storage and place it somewhere else.

One of the first ideas you may have, if you want to solve this problem is to bind them to the clients ip so nobody from outside can use the token, but since we are speaking of IP scarcity and things like DS-Lite, this isn't a really good solution.

The Idea

The only really good solution for this would be to give each browser a unique ID, which only can be manipulated hardly like the MAC address.

Sadly nowadays browsers doesn't provide built in endpoint for this, so we have to built something around the existing APIs.

When you search around the internet for a while you will soon find some projects like AmIUnique, which are using parameters like your computers User Agent or the way of rendering text to distinguish between devices and browsers. When you use some of these indetifiers you can create a unique id for any device and browser as long as there aren't any updates to your system. But because in our case JWTs only need to be valid for some hours or days this should work fine.

Unique ID in React App

To show you how to implement this into your apps I will create a sample react app and use the uniquebrowserid package for creating the unique IDs.

After you initialize your app, you can install the package with npm install uniquebrowserid, open the App.js file and add the following code:


After saving the file and running npm run start you should see your unique browser ID in the app.
Alt Text
To bind this into your JSON Web Tokens, you just need to send the result of the new UID().completeID() function to your backend.

If you want to learn more about the implementation of unique browser IDs into your app, you can visit the packages website at: https://www.npmjs.com/package/uniquebrowserid. There you also find, how to create one time unique IDs.

Discussion (7)

pic
Editor guide
Collapse
crimsonmed profile image
Médéric Burlet

Wouldn't it be better to have your backend perform browser analysis from headers and compare it with whats in the JWT? As generating this kind of uniqueID on the client means it will be sent to the server at one point and it can be highjacked the same way as the JWT?

Collapse
alexanderschau profile image
Alexander Schau Author

You're right, but the header information aren't clear enough. The package contains a one time id function (id which will only live for 30 seconds), which will reduce the moments, where hijackers can steal the id. Sure, it isn't a perfect solution, but it is a more secure one and helps against Local Storage copiers 😆

Collapse
kg_thebest profile image
Kaique Garcia

The real problem here isn't about someone steal the id, but discover how to generate those ids based on how your client-side script's working. Then he/she could do everything generating someone else id's to each fake request.

That's why things should be generated on server-side: to keep the secret key... secret. Hehe.

Collapse
crimsonmed profile image
Médéric Burlet

I would have added or used a user agent middleware on the server side. Compiling user agent information in the JWT and then checking through the middle ware. and if the user agent + headers arent the same then dont process the request.

Collapse
samsch_org profile image
Samuel Scheiderich

This provides only an incomplete small additional security feature.

The correct solution to authentication is to use server-side ("stateful") sessions where you validate the sessionID from the client against your database for every request, and the sessionID is stored only in an httpOnly cookie.

Using an httpOnly cookie provides a complete fix for the security issue described here by making it impossible to steal the token without user-level/physical access to the browser (which isn't generally possible to protect against, including by your method).

Stop using localStorage for authentication: rdegges.com/2018/please-stop-using...
And Stop Using JWTs! gist.github.com/samsch/0d1f3d3b474...

Collapse
abdellani profile image
Mohamed ABDELLANI

Hi,
Thank a lot for sharing.
But I was wondering, are we going to have the same results if we use a package like v4 from uuid npmjs.com/package/uuid ?

Collapse
alexanderschau profile image
Alexander Schau Author

Thanks for your question.
The uuid package only generates random ids. If you are using uniquebrowserid, you can get a unique id for your browser (this doesn't change until you're updating you browser). To make this happen, the library uses unique details about your device. For more information you can visit amiunique.org/fp, they detailed it really clear, how the whole thing works.