Is JWT Token Revocation worth it? - security

I previously asked a long question regarding the security of JWT tokens but I want to focus specifically on JWT token revocation here. I am using JWT as my primary authentication mechanism for authenticating mobile clients of a mobile application. My question is: Is it worth implementing token revocation? Currently, I am using a short lifetime for my tokens and I am relying on TLS to prevent tokens from being stolen by unauthorized users. I have not implemented token revocation. But basically this means that if a token is stolen somehow, there is no way to revoke it. What concerns me more is that when a user logs out of the application, the last token they were using still works if I can't revoke it. And it also means that I cannot place a limit on the number of tokens a user can request since I'm not keeping track of any tokens that are issued. I've seen many applications that just store all issued tokens in the database, allowing them to revoke and regulate tokens. But this just seems to defeat the purpose of using JWT. Is it worth adding such complexity or is my current system secure?
Thanks in advance. I appreciate any help.

Whether it's worth it is difficult for anyone here to assess. It depends on what you are protecting and what risks you're trying to mitigate.
You can use reference tokens if you deem it necessary to be able to revoke the tokens. It does force the services consuming these tokens to talk to the authorization server which degrades scalability and introduces a single point of failure.
There are initiatives being developed to prevent token theft. Take a look at the Token Binding Protocol and Proof Key for Code Exchange by OAuth Public Clients.

I think you should consider the possibility that someone can extract a token, regardless of how you've secured it. It exists on a device you have no control over.
Rather than pass along a token, why don't you negotiate a secret key with the client and your server? They can use that key to sign their requests to your server and you can keep track of those secrets -- even revoke them if someone signs out. This allows you to keep expirations on the signatures low, so even if they are captured they are only good for a couple minutes.

Related

Spring Security - Stolen JWT token

Using Spring Security and JWT:
Is there a way to invalidate the token if it's from a different
device?
How am I supposed to know whether the token provided is a stolen one
? I can easily copy and paste the entire token and provide it
Thanks
Im going to answer this as simple as possible without trying to have a full blown discussion.
Is there a way to invalidate the token if it's from a different device?
No. This is one of the major big reasons why you should not be using JWT's as sessions. They are issued, and then most often their integrity is validated by checking the signature and then, no more.
Session cookies have all their info stored server-side, which means you can invalidate a session, server side, and when someone presents the cookie you can say NO.
Tokens have a lifetime that you can check, but since most implementations are stateless, we have nothing to invalidate server side, so we can't invalidate the JWT. It has to "time out".
There are probably custom solutions etc out there but all major implementations work this way. Owasp talks about different solutions built in token revocation
This problem has been mentioned several times, but developers dont seem to care.
JWTs are dangerous for user sessions
Stop using JWT for sessions
Stop using JWT for sessions part 2
Why JWTs suck as session tokens
How am I supposed to know whether the token provided is a stolen one ? I can easily copy and paste the entire token and provide it
you can't. How do you know someone hijacked your facebook account? well you report it to facebook that it has been hacked. You can't know who is in possession of any token at any time just based on tokens alone.
Thats why we have certificates (TLS), we have other security measures like CORS, CSRF, ip whitelisting, usernames and passwords etc.
This is what is called Zero Trust If someone presents the token, you have trust that they are who they claim to be unless you want to start whitelisting IP-address (but they can be spoofed with vpns etc)
Security is difficult, and sadly you have now understood the difficulty with it.
Token stealing is a thing, you must instead focus on making sure no one can steal the token.

If an OAuth2 access token can be stolen, why isn't it safe to assume that a refresh token can be stolen too?

If we pose a question "Why not use a non-expiring access token, and not bother with a refresh token?", the answer would probably be "Because if an access token is stolen, the malicious actor has X time (the lifetime of the said non-expiring access token) to perform malicious acts on behalf of the user that token was generated for." So the way that problem is solved, as far as I understand, is by, on successful authentication, sending the user a token-pair of a short lived access token, and a longer lived refresh token. I don't see how this isn't just an attempt at circumventing the original problem. The problem apparently lies in the theoretical possibility of the access token being stolen. So that if it ever is, it's validity expires quickly, so the malicious actor can't be authenticated for a long time. In this hypothetical situation, if whoever can steal the access token, why can't they steal the refresh token instead? The usual answers I got were something along the lines of:
"You have to store the refresh token in a safe place." This makes no sense to me. Why wouldn't I store both the access token and refresh token in a "safe place"?
"The access token has a higher chance of being stolen because it's used more often than a refresh token". In this case, I suspect that "stolen" means "sniffed", as in a Man in the Middle attack. I have a few sub-questions about this one. 1. Why is this applicable? Aren't HTTPS headers/body encrypted? If HTTP is assumed in this question, why are we even talking about protecting against vulnerabilities? Which leads nicely into: 2. In practice, how does this "sniffing" of requests look like? Why wouldn't the malicious actor be able "sniff" every single request being sent, and eventually find the refresh token one?
"In a microservice environment, the access token is sent to all services, while the refresh token is sent only to the authorization service/server." This sounds like the most valid of all, but I still have a question. What difference does this make? It sounds like the authorization server is assumed to have greater security than other servers? I guess it makes sense only when taking a statistical approach, because to steal an access token, any of the X servers need to be exploited, while to steal a refresh token, only one server needs to be exploited. Although this is just my assumption, and it somehow doesn't fit in with the point of this security concept. Also, it doesn't seem like this concept was created to solve a server issue.
I guess my question is:
"If we assume that there are any inherent vulnerabilities with the concept of the access token, or with using it, or with how it's stored etc... What makes the refresh token less susceptible to these vulnerabilities?"
Whilst the security properties of both the access token and the refresh token "at rest" are the same indeed, the difference is that "in transit" a refresh token is easier to secure than an access token because of the way it is used, as explained below.
Firstly the access token is only ever sent to the Resource Server(s), the refresh token is only ever used towards a single Authorization Server. A Resource Server is considered less trusted in many scenario's (the Authorization Server is - by design - a trusted component for the Client) and as you mention, there may be a lot of them which may have different levels of security that apply to them.
Secondly, the refresh token flow towards the Authorization Server may use a "rolling refresh" of the refresh token which means that at the time of access token refresh, a new refresh token is issued as well, which invalidates the old refresh token. This is a pretty common implementation pattern for Authorization Servers.
Lastly, and perhaps a bit of a long shot, the access token is used in many more requests than the refresh token so the chances of any vulnerability that applies to the transport layer (timing attacks) are proportionally increased.

JWT, Stateless Authentication, and Security

I am working on an application where scalability is a big concern. In the past I've used session-based authentication, but decided to go with a stateless server this time around in order to facilitate horizontal scaling.
I am not security expert, but in researching JWTs, it began to seem like these are very insecure. The whole reason we hash passwords is so that if our database is compromised, the attacker cannot impersonate a user. With JWT, we store a secret on the server. If the attacker gains access to the secret, can't they impersonate any user they want? Doesn't this mean that using JWTs would have the same level of security as storing plain text passwords?
I have read that people will sometimes use reddis to cross reference JWTs, but then the server isn't stateless, and I fail to see the benefit of using JWTs at all.
Could someone help clarify this issue for me?
Session based authentication systems, at least any that are worth using, also store a secret on the server. Just like the JWT, the secret is used to sign the data stored in the cookie that session based authentication uses. So this is no different than a JWT.
All of this is totally unrelated to password storage, as the password is only used when you don't have a cookie/JWT.
EDIT:
Not sure what to say about using Redis in conjunction with a JWT... What is being stored in Redis, the token? That seems pointless, as all the server needs to know is the secret to decode the token.
Here are some of the benefits to a using a JWT:
It's stateless, as you've already mentioned
It's not subject to CSRF/XSRF attacks. These attacks work by tricking your browser into sending the cookie to a server that didn't generate the cookie. This can't happen w/a JWT b/c the browser doesn't send the JWT automatically like it does w/cookies.
JWT's are standardized. There is a well defined way to generate them, which means that JWT's are more portable and the process has been vetted by the security community.
The server consuming a JWT token (resource server) does not need access to any secret. All it needs is the public key that belongs to the private key with which the token is digitally signed.
The authorization server that issues the token needs to keep its signing key secret obviously. But the nice thing about token based authentication is that this server can be created by an external party with much more resources/expertise to keep these things secret (Google, Facebook, Microsoft etc).
The resource server does not need to check the database to validate the token as you would need in case of username and password. This helps the scalability of the system and takes away a single point of failure.
If a client/user loses the JWT token, an attacker can impersonate the client/user until the token expires. A good reason to keep the lifetime of tokens short.
I don't see the point of storing JWT tokens in in a Reddis cache. There's no need to share tokens between servers as each call comes with a token in the Authorization HTTP header. Storing them in a cache only increases the risk of tokens being stolen.

Best practices for server-side handling of JWT tokens [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 3 years ago.
Improve this question
(spawned from this thread since this is really a question of its own and not specific to NodeJS etc)
I'm implementing a REST API server with authentication, and I have successfully implemented JWT token handling so that a user can login through a /login endpoint with username/password, upon which a JWT token is generated from a server secret and returned to the client. The token is then passed from the client to the server in each authenticated API request, upon which the server secret is used to verify the token.
However, I am trying to understand the best practices for exactly how and to what extent the token should be validated, to make a truly secure system. Exactly what should be involved in "validating" the token? Is it enough that the signature can be verified using the server-secret, or should I also cross-check the token and/or token payload against some data stored in the server?
A token based authentication system will only be as safe as passing username/password in each request provided that it's equally or more difficult to obtain a token than to obtain a user's password. However, in the examples I've seen, the only information required to produce a token is the username and the server-side secret. Doesn't this mean that assuming for a minute that a malicious user gains knowledge of the server secret, he can now produce tokens on behalf of any user, thereby having access not only to one given user as would be the fact if a password was obtained, but in fact to all user accounts?
This brings me to the questions:
1) Should JWT token validation be limited to verifying the signature of the token itself, relying on the integrity of the server secret alone, or accompanied by a separate validation mechanism?
In some cases I've seen the combined use of tokens and server sessions where upon successful login through the /login endpoint a session is established. API requests validate the token, and also compare the decoded data found in the token with some data stored in the session. However, using sessions means using cookies, and in some sense it defeats the purpose of using a token based approach. It also may cause problems for certain clients.
One could imagine the server keeping all tokens currently in use in a memcache or similar, to ensure that even if the server secret is compromised so that an attacker may produce "valid" tokens, only the exact tokens that were generated through the /login endpoint would be accepted. Is this reasonable or just redundant/overkill?
2) If JWT signature verification is the only means of validating tokens, meaning the integrity of the server secret is the breaking point, how should server secrets be managed? Read from an environment variable and created (randomized?) once per deployed stack? Re-newed or rotated periodically (and if so, how to handle existing valid tokens that were created before rotation but needs to be validated after rotation, perhaps it's enough if the server holds on to the current and the previous secret at any given time)? Something else?
Maybe I'm simply being overly paranoid when it comes to the risk of the server secret being compromised, which is of course a more general problem that needs to be addressed in all cryptographic situations...
I've been playing with tokens for my application as well. While I'm not an expert by any means, I can share some of my experiences and thoughts on the matter.
The point of JWTs is essentially integrity. It provides a mechanism for your server verify that the token that was provided to it is genuine and was supplied by your server. The signature generated via your secret is what provides for this. So, yes, if your secret is leaked somehow, that individual can generate tokens that your server would think are its own. A token based system would still be more secure than your username/password system simply because of the signature verification. And in this case, if someone has your secret anyway, your system has other security issues to deal with than someone making fake tokens (and even then, just changing the secret ensures that any tokens made with the old secret are now invalid).
As for payload, the signature will only tell you that the token provided to you was exactly as it was when your server sent it out. verifying the that the payloads contents are valid or appropriate for your application is obviously up to you.
For your questions:
1.) In my limited experience, it's definitely better to verify your tokens with a second system. Simply validating the signature just means that the token was generated with your secret. Storing any created tokens in some sort of DB (redis, memcache/sql/mongo, or some other storage) is a fantastic way of assuring that you only accept tokens that your server has created. In this scenario, even if your secret is leaked, it won't matter too much as any generated tokens won't be valid anyway. This is the approach I'm taking with my system - all generated tokens are stored in a DB (redis) and on each request, I verify that the token is in my DB before I accept it. This way tokens can be revoked for any reason, such as tokens that were released into the wild somehow, user logout, password changes, secret changes, etc.
2.) This is something I don't have much experience in and is something I'm still actively researching as I'm not a security professional. If you find any resources, feel free to post them here! Currently, I'm just using a private key that I load from disk, but obviously that is far from the best or most secure solution.
Here are some things to consider when implementing JWT's in your application:
Keep your JWT lifetime relatively short, and have it's lifetime managed at the server. If you don't, and later on need to require more information in your JWTs, you'll have to either support 2 versions, or wait until your older JWTs have expired before you can implement your change. You can easily manage it on the server if you only look at the iat field in the jwt, and ignore the exp field.
Consider including the url of the request in your JWT. For example, if you want your JWT to be used at endpoint /my/test/path, include a field like 'url':'/my/test/path' in your JWT, to ensure it's only ever used at this path. If you don't, you may find that people start using your JWTs at other endpoints, even ones they weren't created for. You could also consider including an md5(url) instead, as having a big url in the JWT will end up making the JWT that much bigger, and they can get quite big.
JWT expiry should be configurable by each use case if JWTs are being implemented in an API. For example, if you have 10 endpoints for 10 different use cases for JWT's, make sure you can make each endpoint accept JWTs that expire at different times. This allows you to lock down some endpoints more than others, if for example, the data served by one endpoint is very sensitive.
Instead of simply expiring JWTs after a certain time, consider implementing JWTs that support both:
N usages - can only be used N times before they expire and
expire after certain amount of time (if you have a one use only token, you don't want it living forever if not used, do you?)
All JWT authentication failures should generate an "error" response header that states why the JWT authentication failed. e.g. "expired", "no usages left", "revoked", etc. This helps implementers know why their JWT is failing.
Consider ignoring the "header" of your JWTs as they leak information and give a measure of control to hackers. This is mostly concerning the alg field in the header - ignore this and just assume that the header is what you want to support, as this avoids hackers trying to use the None algorithm, which removes the signature security check.
JWT's should include an identifier detailing which app generated the token. For example if your JWT's are being created by 2 different clients, mychat, and myclassifiedsapp, then each should include it's project name or something similar in the "iss" field in the JWT e.g. "iss":"mychat"
JWT's should not be logged in log files. The contents of a JWT can be logged, but not the JWT itself. This ensures devs or others can't grab JWT's from log files and do things to other users accounts.
Ensure your JWT implementation doesn't allow the "None" algorithm, to avoid hackers creating tokens without signing them. This class of errors can be avoided entirely by ignoring the "header" of your JWT.
Strongly consider using iat (issued at) instead of exp (expiry) in your JWTs. Why? Since iat basically means when was the JWT created, this allows you to adjust on the server when the JWT expires, based on the creation date. If someone passes in an exp that's 20 years in the future, the JWT basically lives forever! Note that you automatically expire JWTs if their iat is in the future, but allow for a little bit of wiggle room (e.g 10 seconds), in case the client's time is slightly out of sync with the servers time.
Consider implementing an endpoint for creating JWTs from a json payload, and force all your implementing clients to use this endpoint to create their JWTs. This ensures that you can address any security issues you want with how JWTs are created in one place, easily. We didn't do this straight off in our app, and now have to slowly trickle out JWT server side security updates because our 5 different clients need time to implement. Also, make your create endpoint accept an array of json payloads for JWTs to create, and this will decrease the # of http requests coming in to this endpoint for your clients.
If your JWT's will be used at endpoints that also support use by session, ensure you don't put anything in your JWT that's required to satisfy the request. You can easily do this if you ensure your endpoint works with a session, when no JWT is supplied.
So JWT's generally speaking end up containing a userId or groupId of some sort, and allow access to part of your system based on this information. Make sure you're not allowing users in one area of your app to impersonate other users, especially if this provides access to sensitive data. Why? Well even if your JWT generation process is only accessible to "internal" services, devs or other internal teams could generate JWTs to access data for any user, e.g. the CEO of some random client's company. For example, if your app provides access to financial records for clients, then by generating a JWT, a dev could grab the financial records of any company at all! And if a hacker gets into your internal network in anyway, they could do the same.
If you are are going to allow any url that contains a JWT to be cached in any way, ensure that the permissions for different users are included in the url, and not the JWT. Why? Because users may end up getting data they shouldn't. For example, say a super user logs into your app, and requests the following url: /mysite/userInfo?jwt=XXX, and that this url gets cached. They logout and a couple of minutes later, a regular user logs into your app. They'll get the cached content - with info about a super user! This tends to happen less on the client, and more on the server, especially in cases where you're using a CDN like Akamai, and you're letting some files live longer. This can be fixed by including the relevant user info in the url, and validating this on the server, even for cached requests, for example /mysite/userInfo?id=52&jwt=XXX
If your jwt is intended to be used like a session cookie, and should only work on the same machine the jwt was created for, you should consider adding a jti field to your jwt. This is basically a CSRF token, that ensures your JWT can't be passed from one users's browser to anothers.
I don't think I'm an expert but I'd like to share some thoughs about Jwt.
1: As Akshay said, it's better to have a second system to validate your token.
a.: The way I handle it : I store the hash generated into a session storage with the expiricy time. To validate a token, it needs to have been issued by the server.
b.:There is at least one thing that must be checked the signature method used. eg :
header :
{
"alg": "none",
"typ": "JWT"
}
Some libraries validating JWT would accept this one without checking the hash. That means that without knowing your salt used to sign the token, a hacker could grant himself some rights. Always make sure this can't happen.
https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/
c.: Using a cookie with a session Id would not be useful to validate your token. If someone wants to hijack the session of a lambda user, he would just have to use a sniffer (eg : wireshark). This hacker would have both information at the same time.
2: It is the same for every secret. There is always a way to know it.
The way I handle it is linked to the point 1.a. : I have a secret mixed with a random variable. The secret is unique for every token.
However, I am trying to understand the best practices for exactly how
and to what extent the token should be validated, to make a truly
secure system.
If you want the best security possible, you should not blindly follow best practices. The best way is to understand what you're doing (I think it's ok when I see your question), and then evaluate the security you need. And if the Mossad want to have access to your confidential data, they 'll always find a way. (I like this blog post : https://www.schneier.com/blog/archives/2015/08/mickens_on_secu.html )
Lots of good answers here. I'll integrate some of the answers I think are most relevant and add some more suggestions.
1) Should JWT token validation be limited to verifying the signature of the token itself, relying on the integrity of the server secret alone, or accompanied by a separate validation mechanism?
No, because of reasons unrelated to the compromise of a token secret. Each time a user logs in via a username and password, the authorization server should store either the token that was generated, or metadata about the token that was generated. Think of this metadata as an authorization record. A given user and application pair should only have one valid token, or authorization, at any given time. Useful metadata is the user id associated with the access token, the app id, and the time when the access token was issued (which allows for the revocation of existing access tokens and the issuing of a new access token). On every API request, validate that the token contains the proper metadata. You need to persist information about when each access tokens was issued, so that a user can revoke existing access tokens if their account credentials are compromised, and log in again and start using a new access token. That will update the database with the time when the access token was issued (the authorization time created). On every API request, check that the issue time of the access token is after the authorization time created.
Other security measures included not logging JWTs and requiring a secure signing algorithm like SHA256.
2) If JWT signature verification is the only means of validating tokens, meaning the integrity of the server secret is the breaking point, how should server secrets be managed?
The compromise of server secrets would allow an attacker to issue access tokens for any user, and storing access token data in step 1 would not necessarily prevent the server from accepting those access tokens. For example, say that a user has been issued an access token, and then later on, an attacker generates an access token for that user. The authorization time of the access token would be valid.
Like Akshay Dhalwala says, if your server-side secret is compromised, then you have bigger problems to deal with because that means that an attacker has compromised your internal network, your source code repository, or both.
However, a system to mitigate the damage of a compromised server secret and avoid storing secrets in source code involves token secret rotation using a coordination service like https://zookeeper.apache.org. Use a cron job to generate an app secret every few hours or so (however long your access tokens are valid for), and push the updated secret to Zookeeper. In each application server that needs to know the token secret, configure a ZK client that is updated whenever the ZK node value changes. Store a primary and a secondary secret, and each time the token secret is changed, set the new token secret to the primary and the old token secret to the secondary. That way, existing valid tokens will still be valid because they will be validated against the secondary secret. By the time the secondary secret is replaced with the old primary secret, all of the access tokens issued with the secondary secret would be expired anyways.
IETF have a RFC in progress in the oAuth Working Group see : https://tools.ietf.org/id/draft-ietf-oauth-jwt-bcp-05.html

ASP.Net Web Api 2, json web token, logout and ensure that the server should not authenticate token anymore

I am working on asp.net web api 2 and used JWT for authentication. The application is working fine as it generates token on login request from user, and then user can use that token for subsequent request. But I have some security concerns like
What if the token is stolen from user's browser, How can server detect a valid request among two requests sent from two different computers.
When user will sign out, how server can detect that this particular token is now invalid/loggedout. As I read about log out, it is merely deletion of token from client browser, so stolen token will still be there, requesting from other pc.
How can server revoke a token when expiration period reached?
Please comment if my question is not clear.
Please find the answers as below:
1) Access tokens like cash, if you have it then you can use it, if you have valid access token there is no way to identify if the request is coming Authorized party or not, thats why HTTPS must be used with OAuth 2.0 and bearer tokens.
2) Self contained tokens like JWT are not revocable, so there is no DB checks and this is the beauty of it, you need to leave those tokens until they expire. If you used reference tokens then you will be able to revoke them, but the draw back for this approach is hitting the DB with each API call to validate the token.
3) Already answered in part 2.
You can check my series of posts about this topic using the below links:
Token Based Authentication using ASP.NET Web API 2, Owin, and
Identity.
AngularJS Token Authentication using ASP.NET Web API 2.
JSON Web Token in ASP.NET Web API 2 using Owin.
When it comes to JWT revocation the general idea seems to be either that:
it simply can't be done
or it can be done, but it goes against the stateless nature of JWT.
I generally don't agree with either. First JWT is just a token format (Learn JSON Web Tokens), yes it can be used to shift some state from servers to clients, but that does not impose any restriction on what we can and should do to consider them valid from the point of view of our application.
Second, if you understand the implications and the associated cost of implementing revocation functionality and you think it's worthwhile to use self-contained tokens instead of alternatives that could simplify revocation but increase the complexity elsewhere then you should go for it.
Just one more word on the stateless thing, I think I could only agree to it in the remote chance that the application receiving and validating tokens does not maintain any state at all. In this situation, introducing revocation would mean introducing a persistent store where one did not exist before.
However, most applications already need to maintain some kind of persistent state so adding a few more bits to track blacklisted/invalid tokens is a non-issue. Additionally, you only need to track that information until the token expiration date.
Having covered the general theory, lets go through your individual questions:
If your security requirements mandate that you need to employ additional measures to try to detect malicious use of a token then you're free to do so. A simple example would be blacklisting a token if you detect usage of the same token coming from very different geographical locations.
With support for token revocation in place the application logout scenario would just need to include a step to blacklist the associated token.
I may be missing something here, but if the token expiration time was reached the regular process to validate a JWT would already include a check to make sure that the token was not yet expired.

Resources