I'm building an application with ExpressJS, Mongodb(Mogoose). Application contains routes where user has to be authenticated before accessing it.
Currently I have written a express middleware to do the same. Here with the help of JWT token I'm making mongodb query to check whether user is authenticated or not. but feel this might put unnecessary request load on my database.
should I integrate redis for this specific task?
does it will improve API performance? or should go ahead with existing
mongodb approach?
would be helpful if I get more insights on this.
TLDR: If you want the capability to revoke a JWT at some point, you'll need to look it up. So yes, something fast like Redis can be useful for that.
One of the well documented drawbacks of using JWTs is that there's no simple way to revoke a token if for example a user needs to be logged out or the token has been compromised. Revoking a token would mean to look it up in some storage and then deciding what to do next. Since one of the points of JWTs is to avoid round trips to the db, a good compromise would be to store them in something less taxing than an rdbms. That's a perfect job for Redis.
Note however that having to look up tokens in storage for validity still reintroduces statefulness and negates some of the main benefits of JWTs. To mitigate this drawback make the list a blacklist (or blocklist, i.e. a list of invalid tokens). To validate a token, you look it up on the list and verify that it is not present. You can further improve on space and performance by staggering the lookup steps. For instance, you could have a tiny in-app storage that only tracks the first 2 or 3 bytes of your blacklisted tokens. Then the redis cache would track a slightly larger version of the same tokens (e.g. the first 4 or 5 bytes). You can then store a full version of the blacklisted tokens using a more persistent solution (filesystem, rdbms, etc). This is an optimistic lookup strategy that will quickly confirm that a token is valid (which would be the more common case). If a token happens to match an item in the in-app blacklist (because its first few bytes match), then move on to do an extra lookup on the redis store, then the persistent store if need be. Some (or all) of the stores may be implemented as tries or hash tables. Another efficient and relatively simple to implement data structure to consider is something called a Bloom filter.
As your revoked tokens expire (of old age), a periodic routine can remove them from the stores. Keep your blacklist short and manageable by also shortening the lifespan of your tokens.
Remember that JWTs shine in scenarios where revoking them is the exception. If you routinely blacklist millions of long-lasting tokens, it may indicate that you have a different problem.
You can use Redis for storing jwt label. Redis is much faster and convenient for storing such data. The request to Redis should not greatly affect the performance. You can try the library jwt-redis
JWT contains claims. you can store a claim such as
session : guid
and maintain a set in redis for all keys black listed. the key should stay in set as long as the jwt validity.
when your api is hit
verify jwt signature. if tempered stop
extract claims in a list of key value pairs
get the session key and check in redis in blacklisted set
if found, stop else continue
Related
For authentication in my REST API, I am using JWTs, which only contain the client user's UUID.
To check if the user is not blocked or has rights to execute a specific endpoint, I fetch the user's data from a PostgreSQL database using the UUID of the JWT, and verify if the user is allowed to execute.
Is it a good practice to do a database operation for securing every API call?
Also thought about storing roles and rights in the JWTs but what if these roles and rights change in the database and the JWTs data becomes stale? Is there any solution to the stale data?
You have to check some data store for staleness with JWT.
If you conclude with performance tests and measurements that accessing a PostgreSQL DB for every request would be too slow, then you optimize that part. Maybe optimize your DB performance by changing the data structure or configuration. Or use a completely different database just for this purpose. Bad practice would be trying to optimize this prematurely.
Here's an article that talks about how to manually expire JWTs, it describes an example with using Redis:
An implementation would probably be, to store a so-called “blacklist” of all the tokens that are valid no more and have not expired yet. You can use a DB that has TTL option on documents which would be set to the amount of time left until the token is expired. Redis is a good option for this, that will allow fast in memory access to the list. Then, in a middleware of some kind that runs on every authorized request, you should check if provided token is in The Blacklist.
TL DR: No its not :-)
In general JWT's are not the most suitable solution for scenario you describe, you are already facing the first trouble of it now.
Because you better store stateless information in your JWT, otherwise you would be better of using other authentication solutions.
Conclusion
Stateless JWT tokens cannot be invalidated or updated, and will introduce either size issues or security issues depending on where you store them. Stateful JWT tokens are functionally the same as session cookies, but without the battle-tested and well-reviewed implementations or client support.
Unless you work on a Reddit-scale application, there's no reason to be using JWT tokens as a session mechanism. Just use sessions.
source: http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/
And some other related posts:
http://cryto.net/~joepie91/blog/2016/06/19/stop-using-jwt-for-sessions-part-2-why-your-solution-doesnt-work/
https://medium.com/#yuliaoletskaya/can-jwt-be-used-for-sessions-4164d124fe23
https://logrocket.com/blog/jwt-authentication-best-practices/
Most security consultants / auditors I spoke to also are very strong in there statements about JWT, they are widely overused and have a specific problem domain.
But if you really want/need to, I would follow #ekuusela's answer, especially given:
Bad practice would be trying to optimize this prematurely.
I’m learning to code for one year now. I mainly learned how to deal with a rest API (Node/Express on back-end and Vue on front-end).
I get to the point where I want to develop the ideas I have for app.
For this, I first wanted to develop the backend to have a authentification process that I could use as a boilerplate for other project I would have.
But now I’m completely lost with Jsonwebtoken and how to exactly use it in order to make something secure as well as user-friendly.
So far I understand that a rest API should be stateless (I.e. nothing should be store server-side and should therefore not have DB calls -as for sessions- to grant access to data).
In this respect, I’ve noted different strategies :
Short-lived JWT : (+) this is very secure since you theoretically have to log in every time you want to access the server (-) very bad user experience
Long-lived JWT : (+) user-friendly (persistent login) (-) very insecure (no way to check if JWT was stolen)
Short-lived JWT with Long-lived Refresh Token…
That’s where I get confused…
From every articles/tutorials I’ve read, the refresh token should be linked somehow with a DB (e.g. to store the refresh token key or the blacklisted token…). I’ve also seen a tutorial that partly linked the secret key (to verify the token) with the hashed password stored in the DB. This is kind of smart since previous token will automatically be considered as invalid as of the moment the user changes his password… But this once again mean a DB call…
My point is that I’m coming to the conclusion that
(1) there’s no perfect way to handle authentification process in secure and user-friendly way…
(2) DB calls cannot be avoided to have something a bit secure...
And considering that conclusion, I definitely can’t understand the use of refresh token…
If refresh tokens required DB calls, you could get to the same result with only one main token…
You could for instance store a JWT ID in the token and in the DB… If those two id match upon validation of the token, you issue a new token with a new id that overwrites the previous one… Now if you use an old one, it will never be validated… Then, since you have called the DB to validate the token (most certainly the USER table), you could check in the sametime if, for example, the user is an admin or not (no need to store it in the JWT)… Finally, you could use the « hashed password » trick described above to enhance security…
So… What am I missing ? What is the best strategy ?
I’ll be happy to have your comments on this (or a link to a very good article - I’ve a lot of these though…)
Thank you very much for your help
PS: and I’m not even talking about how to send the token to the server (with cookie but risk of CSRF attach or with header but subject to XSS attack if token is stored client-side)… In this respect I’ve seen multiple tutorial that use JWT through cookie with cerf key stored client side as well as inside the jet => both should be send.
PS2: I hope I'm clear since I'm french-speaking native :-)
So you have asked quite a few questions in this one question. It will be quite difficult for anyone to give a thoughtful answer here, but I shall try. Before that, full disclaimer, that I am the author of a new library called SuperTokens that I believe would provide you the best solution for session management. It's possible that you may have already read our blog: https://hackernoon.com/all-you-need-to-know-about-user-session-security-ee5245e6bdad. It's best if we can chat about this so that I can give you a full detailed explanation about everything you have asked (I would love to help out). Join our discord: https://discord.gg/zVcVeev
Now to answer your question(s):
You should always only use short lived JWTs
Doing a database call for authentication is not a problem, but as everything else, we try and optimise things, so doing fewer db calls, is better. If you go with JWT access tokens and Opaque Refresh tokens, then for most APIs, you do not need to do a db call. However, if you link the secret key of the JWT with the hashed password, then you would have to a db call for every API - which is OK, but I feel unnecessary since you are going to use short lived JWTs anyways (a few hours to a day max)
You mentioned token theft - this is a tricky issue, but according to RFC 6819, you can use the concept of rotating refresh token to detect theft. Of course, actually doing so can be tricky as you have to take care of many race conditions and network failure issues - see https://hackernoon.com/the-best-way-to-securely-manage-user-sessions-91f27eeef460
About why we need refresh tokens: Say you do not have a refresh token, then you would have just one token that you could treat as both, an access and a refresh token. You could still make it short lived (when it's an access token), and then after it expires, treat it as a refresh token - which also has some lifetime. The problem with this is that it's not very clean (point of expiring a token is that it is useless afterwards), and that you are exposing the refresh token over the network every single API call - reducing security. I know you can have HTTPS, but there are HTTPS MITM attacks as well - see the blog post, part 1.
In terms of storage, one neat trick could be to split the access token into two - one to store in secure, httponly cookie, and one to store in localstorage. For every API call, send both to the server (cookies will be sent automatically anyways), and then the server would combine the two and go about authenticating. This would prevent both, CSRF and XSS attacks on sessions!
Now, you could either implement this whole thing on your own, or then use our library that does all these things out of the box: https://github.com/supertokens/supertokens-node-mysql-ref-jwt.
To discuss this more, or any other questions you have, join our discord server.
PS: I know I used this for advertising my lib, but I hope I did answer your question. It is genuinely difficult to give a good explanation to your questions without having a conversation.
I generate oauth2 access and refresh tokens and store them in my database. I generate these tokens using UUID v4 and remove the dashes. I used to delete the tokens after they expire but now I store all of them because I thought of something that could happen.
What if an attacker stored locally all of the access tokens that were generated for him and he kept using these access tokens again and again for authorization. Since I, as the DB admin, was deleting the generated tokens, the DB has no way of knowing that the token is unique. Therefore if the UUIDv4 algorithm generates an access token for a different user and it's a collision (same UUID as one generated previously) and the attacker found that collision, he could get into the service since he has the tokens that have been generated before.
My question is should I worry about this and keep all my tokens in case of collision to check for uniqueness or should I delete the access and refresh tokens after they expire and trust that UUIDv4 has enough entropy to prevent this?
I'm also worried that if I keep all the tokens, it's going to inflate the database since the access tokens expire every hour and are regenerated the next time the user takes an action.
Any help is appreciated!
Though there is a minimal chance of UUID collisions in theory, the whole point of UUIDs is to be unique in practice, just consider the name "Universally Unique Identifier". For calculating the chances of a collision, see e.g. this link, I wouldn't sit and wait until it happens. ;-)
I don't expect that you generate enough UUIDs for a realistic chance of even a single collision. If you still do so, storing that many access tokens in a database might also be a problem, it might need huge storage. If you still afraid of collisions, I suggest generating something even more unique than UUIDs, but avoid storing all of them in your database.
When it comes to remember me cookies, there are 2 distinct approaches:
Hashes
The remember me cookie stores a string that can identify the user (i.e. user ID) and a string that can prove that the identified user is the one it pretends to be - usually a hash based on the user password.
Tokens
The remember me cookie stores a random (meaningless), yet unique string that corresponds with with a record in a tokens table, that stores a user ID.
Which approach is more secure and what are its disadvantages?
You should use randomly generated tokens if possible. Of course, the downside is that you have to write some extra code to store and use them on the server side, so this might not be warranted for all web applications. But from a security standpoint, this has distinct advantages:
An attacker cannot generate tokens from user IDs, but he can definitely generate hashes. This is a big problem, even if you use salt when generating hashes (and you should), your users are screwed if the salt ever gets into the wrong hands.
Giving out these tokens enables your users (or your admin if need be) to "log out" certain sessions that they might want to get rid of. This is actually a cool feature to have, Google and Facebook use it for example.
So, if you have time and budget: tokens, absolutely.
Typically you keep the token -> user mapping secure on the server side. So ultimately your security is all based around keeping the token safe and ensuring that its lifetime is controlled (e.g. it expires and/or is only valid when given to you from the same IP as that used by the original provider of the credentials - again, just an example)
Security of token based authentication
Hope this helps.
Yes tokens would be more secure if they produce a random string each time.
On the other hand, the whole point of remember me is that the user doesn't have to log in again, so unless they click log out your rarely going to need to re-produce a new token unless it expires.
I guess you should stick with tokens and not sacrifice security for lazyness :-p
In an effort to increase performance, I was thinking of trying to eliminate a plain 'session cookie', but encrypt all the information in the cookie itself.
A very simple example:
userid= 12345
time=now()
signature = hmac('SHA1',userid + ":" + time, secret);
cookie = userid + ':' + time + ':' + signature;
The time would be used for a maximum expirytime, so cookies won't live on forever.
Now for the big question: is this a bad idea?
Am I better off using AES256 instead? In my case the data is not confidential, but it must not be changed under any circumstances.
EDIT
After some good critique and comments, I'd like to add this:
The 'secret' would be unique per-user and unpredictable (random string + user id ?)
The cookie will expire automatically (this is done based on the time value + a certain amount of seconds).
If a user changes their password, (or perhaps even logs out?) the secret should change.
A last note: I'm trying come up with solutions to decrease database load. This is only one of the solutions I'm investigating, but it's kind of my favourite. The main reason is that I don't have to look into other storage mechanism better suited for this kind of data (memcache, nosql) and it makes the web application a bit more 'stateless'.
10 years later edit
JWT is now a thing.
A signed token is a good method for anything where you want to issue a token and then, when it is returned, be able to verify that you issued the token, without having to store any data on the server side. This is good for features like:
time-limited-account-login;
password-resetting;
anti-XSRF forms;
time-limited-form-submission (anti-spam).
It's not in itself a replacement for a session cookie, but if it can eliminate the need for any session storage at all that's probably a good thing, even if the performance difference isn't going to be huge.
HMAC is one reasonable way of generating a signed token. It's not going to be the fastest; you may be able to get away with a simple hash if you know about and can avoid extension attacks. I'll leave you to decide whether that's worth the risk for you.
I'm assuming that hmac() in whatever language it is you're using has been set up to use a suitable server-side secret key, without which you can't have a secure signed token. This secret must be strong and well-protected if you are to base your whole authentication system around it. If you have to change it, everyone gets logged out.
For login and password-resetting purposes you may want to add an extra factor to the token, a password generation number. You can re-use the salt of the hashed password in the database for this if you like. The idea is that when the user changes passwords it should invalidate any issued tokens (except for the cookie on the browser doing the password change, which gets replaced with a re-issued one). Otherwise, a user discovering their account has been compromised cannot lock other parties out.
I know this question is very old now but I thought it might be a good idea to update the answers with a more current response. For anyone like myself who may stumble across it.
In an effort to increase performance, I was thinking of trying to
eliminate a plain 'session cookie', but encrypt all the information in
the cookie itself.
Now for the big question: is this a bad idea?
The short answer is: No it's not a bad idea, in fact this is a really good idea and has become an industry standard.
The long answer is: It depends on your implementation. Sessions are great, they are fast, they are simple and they are easily secured. Where as a stateless system works well however, is a bit more involved to deploy and may be outside the scope of smaller projects.
Implementing an authentication system based on Tokens (cookies) is very common now and works exceedingly well for stateless systems/apis. This makes it possible to authenticate for many different applications with a single account. ie. login to {unaffiliated site} with Facebook / Google.
Implementing an oAuth system like this is a BIG subject in and of itself. So I'll leave you with some documentation oAuth2 Docs. I also recommend looking into Json Web Tokens (JWT).
extra
A last note: I'm trying come up with solutions to decrease database
load. This is only one of the solutions I'm investigating
Redis would work well for offloading database queries. Redis is an in memory simple storage system. Very fast, ~temporary storage that can help reduce DB hits.
Update: This answer pertains to the question that was actually asked, not to an imagined history where this question was really about JWT.
The most important deviations from today's signed tokens are:
The question as originally posed didn't evince any understanding of the need for a secret in token generation. Key management is vital for JWT.
The questioner stated that they could not use HTTPS, and so they lacked confidentiality for the token and binding between the token and the request. In the same way, even full-fledged JWT can't secure a plain HTTP request.
When the question was revised to explain how a secret could be incorporated, the secret chosen required server-side state, and so fell short of the statelessness provided by something like JWT.
Even today, this homebrew approach would be a bad idea. Follow a standard like JWT, where both the scheme and its implementations have been carefully scrutinized and refined.
Yes, this is a bad idea.
For starters, it's not secure. With this scheme, an attacker can generate their own cookie and impersonate any user.
Session identifiers should be chosen from a large (128-bit) space by a cryptographic random number generator.
They should be kept private, so that attackers cannot steal them and impersonate an authenticated user. Any request that performs an action that requires authorization should be tamper-proof. That is, the entire request must have some kind of integrity protection such as an HMAC so that its contents can't be altered. For web applications, these requirements lead inexorably to HTTPS.
What performance concerns do you have? I've never seen a web application where proper security created any sort of hotspot.
If the channel doesn't have privacy and integrity, you open yourself up to man-in-the-middle attacks. For example, without privacy, Alice sends her password to Bob. Eve snoops it and can log in later as Alice. Or, with partial integrity, Alice attaches her signed cookie to a purchase request and sends them to Bob. Eve intercepts the request and modifies the shipping address. Bob validates the MAC on the cookie, but can't detect that the address has been altered.
I don't have any numbers, but it seems to me that the opportunities for man-in-the-middle attacks are constantly growing. I notice restaurants using the wi-fi network they make available to customers for their credit-card processing. People at libraries and in work-places are often susceptible to sniffing if their traffic isn't over HTTPS.
You should not reinvent the wheel. The session handler that comes with your development platform far is more secure and certainly easier to implement. Cookies should always be very large random numbers that links to server side data. A cookie that contains a user id and time stamp doesn't help harden the session from attack.
This proposed session handler is more vulnerable to attack than using a Cryptographic nonce for each session. An attack scenario is as follows.
It is likely that you are using the same secret for your HMAC calculation for all sessions. Thus this secret could be brute forced by an attacker logging in with his own account. By looking at his session id he can obtain everything except for the secret. Then the attacker could brute force the secret until the hmac value can be reproduced. Using this secret he can rebuild a administrative cookie and change his user_id=1, which will probably grant him administrative access.
What makes you think this will improve performance vs. secure session IDs and retrieving the userid and time information from the server-side component of the session?
If something must be tamper-proof, don't put it in the toddlers' hands. As in, don't give it to the client at all, even with the tamper-proof locking.
Ignoring the ideological issues, this looks pretty decent. You don't have a nonce. You should add that. Just some random garbage that you store along with the userid and time, to prevent replay or prediction.