Invalidating JSON Web Tokens

前端 未结 28 2422
夕颜
夕颜 2020-11-22 06:17

For a new node.js project I\'m working on, I\'m thinking about switching over from a cookie based session approach (by this, I mean, storing an id to a key-value store conta

相关标签:
28条回答
  • An approach I've been considering is to always have an iat (issued at) value in the JWT. Then when a user logs out, store that timestamp on the user record. When validating the JWT just compare the iat to the last logged out timestamp. If the iat is older, then it's not valid. Yes, you have to go to the DB, but I'll always be pulling the user record anyway if the JWT is otherwise valid.

    The major downside I see to this is that it'd log them out of all their sessions if they're in multiple browsers, or have a mobile client too.

    This could also be a nice mechanism for invalidating all JWTs in a system. Part of the check could be against a global timestamp of the last valid iat time.

    0 讨论(0)
  • 2020-11-22 06:52

    Why not just use the jti claim (nonce) and store that in a list as a user record field (db dependant, but at very least a comma-separated list is fine)? No need for separate lookup, as others have pointed out presumably you want to get the user record anyway, and this way you can have multiple valid tokens for different client instances ("logout everywhere" can reset the list to empty)

    0 讨论(0)
  • 2020-11-22 06:54

    This seems really difficult to solve without a DB lookup upon every token verification. The alternative I can think of is keeping a blacklist of invalidated tokens server-side; which should be updated on a database whenever a change happens to persist the changes across restarts, by making the server check the database upon restart to load the current blacklist.

    But if you keep it in the server memory (a global variable of sorts) then it's not gonna be scalable across multiple servers if you are using more than one, so in that case you can keep it on a shared Redis cache, which should be set-up to persist the data somewhere (database? filesystem?) in case it has to be restarted, and every time a new server is spun up it has to subscribe to the Redis cache.

    Alternative to a black-list, using the same solution, you can do it with a hash saved in redis per session as this other answer points out (not sure that would be more efficient with many users logging in though).

    Does it sound awfully complicated? it does to me!

    Disclaimer: I have not used Redis.

    0 讨论(0)
  • 2020-11-22 06:55

    An alternative would be to have a middleware script just for critical API endpoints.
    This middleware script would check in the database if the token is invalidated by an admin.
    This solution may be useful for cases where is not necessary to completely block the access of a user right away.

    0 讨论(0)
  • 2020-11-22 06:56

    This is primarily a long comment supporting and building on the answer by @mattway

    Given:

    Some of the other proposed solutions on this page advocate hitting the datastore on every request. If you hit the main datastore to validate every authentication request, then I see less reason to use JWT instead of other established token authentication mechanisms. You've essentially made JWT stateful, instead of stateless if you go to the datastore each time.

    (If your site receives a high volume of unauthorized requests, then JWT would deny them without hitting the datastore, which is helpful. There are probably other use cases like that.)

    Given:

    Truly stateless JWT authentication cannot be achieved for a typical, real world web app because stateless JWT does not have a way to provide immediate and secure support for the following important use cases:

    User's account is deleted/blocked/suspended.

    User's password is changed.

    User's roles or permissions are changed.

    User is logged out by admin.

    Any other application critical data in the JWT token is changed by the site admin.

    You cannot wait for token expiration in these cases. The token invalidation must occur immediately. Also, you cannot trust the client not to keep and use a copy of the old token, whether with malicious intent or not.

    Therefore: I think the answer from @matt-way, #2 TokenBlackList, would be most efficient way to add the required state to JWT based authentication.

    You have a blacklist that holds these tokens until their expiration date is hit. The list of tokens will be quite small compared to the total number of users, since it only has to keep blacklisted tokens until their expiration. I'd implement by putting invalidated tokens in redis, memcached or another in-memory datastore that supports setting an expiration time on a key.

    You still have to make a call to your in-memory db for every authentication request that passes initial JWT auth, but you don't have to store keys for your entire set of users in there. (Which may or may not be a big deal for a given site.)

    0 讨论(0)
  • 2020-11-22 06:56

    USING REFRESHING OF JWT...

    An approach that I take as being practical is to store a refresh token (which can be a GUID) and a counterpart refresh token ID (that does not change no matter how many refreshes are done) on the database and add them as claims for the user when the user's JWT is being generated. An alternative to a database can be used, e.g. memory cache. But I'm using database in this answer.

    Then, create a JWT refresh Web API endpoint that the client can call before the expiry of the JWT. When the refresh is called, get the refresh token from the claims in the JWT.

    On any call to the JWT refresh endpoint, validate the current refresh token and the refresh token ID as a pair on the database. Generate a new refresh token, and use it to replace the old refresh token on the database, using the refresh token ID. Remember they are claims that can be extracted from the JWT

    Extract the user's claims from the current JWT. Begin the process of generating a new JWT. Replace the value of the old refresh token claim with the newly generated refresh token that has also been newly saved on the database. With all that, generate the new JWT and send it to the client.

    So, after a refresh token has been used, whether by the intended user or an attacker, any other attempt to use a/the refresh token, that is not paired, on the database, with its refresh token ID, would not lead to the generation of a new JWT, hence preventing any client having that refresh token ID from being able to use the backend anymore, leading to a full logout of such clients (including the legitimate client).

    That explains the basic information.

    The next thing to add to that is to have a window for when a JWT can be refreshed, such that anything outside that window would be a suspicious activity. For example, the window can be 10min before the expiration of a JWT. The date-time a JWT was generated can be saved as a claim in that JWT itself. And when such suspicious activity occurs, i.e. when someone else tries to reuse that refresh token ID outside or within the window after it has already been used within the window, should mark the refresh token ID as invalid. Hence, even the valid owner of the refresh token ID would have to log in afresh.

    A refresh token that can't be found to be paired, on the database, with a presented refresh token ID implies that the refresh token ID should be invalidated. Because an idle user may try to use a refresh token that an attacker, for example, has already used.

    A JWT that was stolen and used by an attacker, before the intended user does, would be marked as invalid too when the user attempts to use the refresh token too, as explained earlier.

    The only situation not covered is if a client never attempts to refresh its JWT even after an attacker may have already stolen it. But this is unlikely to happen to a client that's not in custody (or something similar) of an attacker, meaning that the client cannot be predicted by the attacker as regards when the client would stop using the backend.

    If the client initiates a usual logout. The logout should be made to delete the refresh token ID and associated records from the database, hence, preventing any client from generating a refresh JWT.

    0 讨论(0)
提交回复
热议问题