What if JWT is stolen?
Asked Answered
P

8

294

I am trying to implement stateless authentication with JWT for my RESTful APIs.

AFAIK, JWT is basically an encrypted string passed as HTTP headers during a REST call.

But what if there's an eavesdropper who see the request and steals the token? Then he will be able to fake request with my identity?

Actually, this concern applies to all token-based authentication.

How to prevent that? A secure channel like HTTPS?

Pretender answered 14/12, 2015 at 3:16 Comment(6)
This is why tokens are often only valid for a short period of time. And yes, you should use HTTPS if you are concerned about the confidentiality of your data.Unset
@JonathonReinhart But if a token expires soon, my client will have to get a new token by re-authenticating himself from time to time. Isn't it kind of tedious?Pretender
@JonathonReinhart I think I get the point why token is short-lived. Because that way, the server doesn't need to keep track of the expiration of a token and thus make way for scalability. It's kind of a trade-off between having finer control of token expiration and having better scalability.Pretender
According to here (stormpath.com/blog/…), seems HTTPS is necessary.Pretender
Can this also help? - "A common security mechanism for detecting token theft is to keep track of request IP address origins." - described in detail in last section here - firebase.google.com/docs/auth/admin/manage-sessionsHaruspicy
Theoretically, it's impossible to prevent token theft. The best we can do is detect that that has happened and then revoke the session ASAP. The best method for detection is to use rotating refresh tokens (as suggested by RFC 6819). Here is a blog that explains this in detail: supertokens.io/blog/…Ridgeway
C
392

I'm the author of a node library that handles authentication in quite some depth, express-stormpath, so I'll chime in with some information here.

First off, JWTs are typically NOT encrypted. While there is a way to encrypt JWTs (see: JWEs), this is not very common in practice for many reasons.

Next up, any form of authentication (using JWTs or not), is subject to MitM attacks (man-in-the-middle) attacks. These attacks happen when an attacker can VIEW YOUR NETWORK traffic as you make requests over the internet. This is what your ISP can see, the NSA, etc.

This is what SSL helps prevent against: by encrypting your NETWORK traffic from your computer -> some server when authenticating, a third party who is monitoring your network traffic can NOT see your tokens, passwords, or anything like that unless they're somehow able to get a copy of the server's private SSL key (unlikely). This is the reason SSL is MANDATORY for all forms of authentication.

Let's say, however, that someone is able to exploit your SSL and is able to view your token: the answer to your question is that YES, the attacker will be able to use that token to impersonate you and make requests to your server.

Now, this is where protocols come in.

JWTs are just one standard for an authentication token. They can be used for pretty much anything. The reason JWTs are sort of cool is that you can embed extra information in them, and you can validate that nobody has messed with it (signing).

HOWEVER, JWTs themselves have nothing to do with 'security'. For all intents and purposes, JWTs are more or less the same thing as API keys: just random strings that you use to authenticate against some server somewhere.

What makes your question more interesting is the protocol being used (most likely OAuth2).

The way OAuth2 works is that it was designed to give clients TEMPORARY tokens (like JWTs!) for authentication for a SHORT PERIOD OF TIME ONLY!

The idea is that if your token gets stolen, the attacker can only use it for a short period of time.

With OAuth2, you have to re-authenticate yourself with the server every so often by supplying your username/password OR API credentials and then getting a token back in exchange.

Because this process happens every now and then, your tokens will frequently change, making it harder for attackers to constantly impersonate you without going through great trouble.

Hopefully this helps ^^

Canaster answered 24/1, 2016 at 23:56 Comment(8)
Since you mention OAuth, one related question: what if attacker steals my oauth token and refresh token... they can theoretically gain access to my API forever (unless I revoke access to that token) correct?Cyclist
Usually refresh tokens have a set expiration as well (although it will be longer than an access token). But yes, you are correct.Canaster
Author of the following article argues that a disadvantage of JWT is that the only way to recover from a stolen JWT is to generate a new key-pair and effectively log all users out. Whereas with session-ids stored in a DB the website could delete only the sessions of the affected user and log him out of all devices. I am not sure how OAuth2 fits in the picture here or whether it helps to mitigate the disadvantages presented. medium.com/@rahulgolwalkar/…Haematogenesis
The author is incorrect. There are different design patterns you can use to invalidate tokens. But in general: using a JWT for any sort of authentication purpose is a bad idea. It's far more efficient to use a session cookie with a session idea embedded inside that is cryptographically signed.Canaster
@Canaster please tell me how JWT is bad idea for authentication? and how i can use session cookie that u mentioned in your comment above?Rebroadcast
This is way too long to type in a single response. If you want to learn more, I've given a detailed talk on the subject. You can check out my slides online: speakerdeck.com/rdegges/jwts-suck-and-are-stupidCanaster
Theoretically, it's impossible to prevent token theft. The best we can do is detect that that has happened and then revoke the session ASAP. The best method for detection is to use rotating refresh tokens (as suggested by RFC 6819). Here is a blog that explains this in detail: supertokens.io/blog/…Ridgeway
Put anti-theft claim in your JWT as hashed info of the system from which the user logged in and check it after the JWT token is checked. This will fail only if MitM attacker simulates the computer of the victim.Window
M
39

I know this is an old question but I think I can drop my $0.50 here, probably someone can improve or provide an argument to totally decline my approach. I'm using JWTs in a RESTful API over HTTPS (ofc).

For this to work, you should always issue short-lived tokens (depends on most cases, in my app I'm actually setting the exp claim to 30 minutes, and ttl to 3 days, so you can refresh this token as long as its ttl is still valid and the token has not been blacklisted)

For the authentication service, in order to invalidate tokens, I like to use an in-memory cache layer (redis in my case) as a JWT blacklist/ban-list in front, depending on some criterias: (I know it breaks the RESTful philosophy, but the stored documents are really short-lived, as I blacklist for their remaining time-to-live -ttl claim-)

Note: blacklisted tokens can't be automatically refreshed

  • If user.password or user.email has been updated (requires password confirmation), auth service returns a refreshed token and invalidates (blacklist) previous one(s), so if your client detects that user's identity has been compromised somehow, you can ask that user to change its password. If you don't want to use the blacklist for it, you can (but I don't encourage you to) validate the iat (issued at) claim against user.updated_at field (if jwt.iat < user.updated_at then JWT is not valid).
  • User deliberately logged out.

Finally you validate the token normally as everybody does.

Note 2: instead of using the token itself (which is really long) as the cache's key, I suggest generating and using a UUID token for the jti claim. Which is good and I think (not sure since it just came up in my mind) you can use this same UUID as the CSRF token as well, by returning a secure / non-http-only cookie with it and properly implementing the X-XSRF-TOKEN header using js. This way you avoid the computing work of creating yet another token for CSRF checks.

Malita answered 6/2, 2018 at 15:58 Comment(4)
If you store a blacklist on server that need to be checked for every request, why not simply use plain old session?Southpaw
@FranklinYu A blacklist is way "cheaper" than a full session store. Since you're storing short-lived key-value objects (depending on their remaining time-to-live, which should be pretty short), and that happens only for sign out actions and such actions that invalidates tokens, so not every token is stored ofc.Malita
How cheap can it be? First of all, if you are still storing anything on server side, you don’t enjoy the “scalability” benefit claimed by JWT because there is still a central blacklist server that all the application server need to talk to before doing anything. If you only need to store 1k blacklist due to quick expiration, you can do the same for sessions and therefore only need to store 1k sessions.Southpaw
I like this approach. You don't actually have to check the blacklist on each request, only on a request that happens after the JWT is expired (which you can read from the token itself) and up to the TTL period after. In a "standard" use case, that should happen, at most, once in the lifetime of a given token. Once refreshed, you can probably decline any future refresh requests. Thanks @MalitaCoastward
C
12

Sorry being a little late on this, but had the similar concerns and now want to contribute something on the same.

1) rdegges added an excellent point, that JWT has nothing to do with the "security" and simply validates, if anyone has messed up with the payload or not(signing); ssl helps to prevent against breaches.

2) Now, if ssl is also somehow compromised, any eavesdropper can steal our bearer token (JWT) and impersonate the genuine user, a next level step what can be done is, to seek the "proof of possession" of JWT from the client.

3) Now, with this approach, presenter of the JWT possess a particular Proof-Of-Possession(POP) key, which the recipient can cryptographically confirm whether the request is from the same authentic user or not.

I referred Proof of Possesion article for this and am convinced with the apporach.

I will be delighted, if able to contribute anything.

Cheers (y)

Clap answered 13/6, 2019 at 10:43 Comment(0)
V
10

To deal with the problem that tokens are getting stolen, you map each JWT with the list of valid IPs.

For eg, when the user logs in with a particular IP when you can add that IP as valid IP for that JWT, and when you get the request pf this JWT from another IP (either the user changed the internet or JWT is stolen, or any reason) you can do the following depending on you use case:

  1. Map CSRF token with user token and incase it gets stolen then it's CSRF token will not match in that you can invalidate that user token.
  2. You can provide a captcha to the user to validate if he is a valid user or not. If he enters the captcha then add that IP to the valid list of that JWT.
  3. You can log out the user and make a new request to log in again.
  4. You can alert the user that your IP has changed or requested from a different location.

You can also use cache with a small expiry of 5 mins in above use-cases instead of checking each and every time.

Suggest if it can be improved.

Variolite answered 15/11, 2020 at 14:24 Comment(3)
Your instincts are good but search for is "Token Binding" this binds the JWT to the TLS session.Comedian
Not an efficient approach. My home wifi's public IP changes almost everyday. Means, I would have to apply any of the three mentioned points everyday.Cloven
@Puspender It entirely depends on the use case, how secure you want to keep things, eg. Banking sites usually use this approach, I guess Discord use this approach at every login.Variolite
A
3

Can't we just add the ip of the initial host which has requested to generate this JWT token as part of the claim ? Now when the JWT is stolen and used from a different machine, when the server validates this token, we could verify if the requested machine ip matches with the one set as part of the claim. This would not match and hence the token can be rejected. Also if the user tries manipulate the token by setting his own ip to the token, the token would be rejected as the token is altered.

Ania answered 4/2, 2020 at 14:14 Comment(5)
That is one possible solution but for clients behind a firewall its typical for an IP address to be picked from a pool of addresses and that can change at any time.Contentious
What if there is forwarded proxy? or the user use VPN every time he connects. not worth approach.Gasoline
Mobile users are also a concern. My mobile IP changes many times per day.Temptress
Only works for devices behind a fixed IP.. It also fails to prevent, say ColleagueA stealing a JWT from ColleagueB (both colleages working on an office network behind the same public IP)Candescent
In my case the clients of the api's are mobile phones and they change ip fast since they may be on wifi and on 4G. That is not an option.Horror
F
1

Once the token gets stolen - it is game over. However there is a way to make it harder to make use of a stolen token.

Check https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_for_Java_Cheat_Sheet.html#token-sidejacking for reference.

Basically, you create a x-Byte long fingerprint in hexadezimal, store its raw value in the token - hash the fingerprint using for example SHA-512 and put the hashed fingerprint inside a httponly secure cookie.

Now instead of validating just the signature and expired date of the token you need to also validate the existence of the cookie and be sure that the raw fingerprint values match.

Flatulent answered 16/6, 2022 at 10:16 Comment(0)
L
0

Client should use part of the hash of user password to encrypt the time that the http msg was sent by client to the server. This part of the hash should also be encrypted with some server secret key into the token when it is created.

The server than can decrypt the http request time and verify for short time delay.

The token is going to change every request.

Landwehr answered 7/4, 2021 at 4:8 Comment(0)
L
-1

Why not use OTP? Yes the token is stolen and someone is impersonating me. But for critical operations, say things that involve payment or update data in my profile, why not an OTP? Obviously this depends on what you are doing with your api, something like a facebook app you should not care to much when someone is reading info, but when they change your account password or remove someone from a group, OTP. For a bank app, otp every time you login and every time you pay.

This is just my 2 cents

Landward answered 10/10, 2023 at 16:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.