4

I have a general question regarding JSON Web Token (JWT).

If the JWT is stolen from the client (say, it was stored as a cookie or the app's database) by hacking or physical access, it can be used to send to the server which the server will think it is the legitimate user. Is this correct?

Is there any common or standard practice to guard against this, for example, by sending the type of device/browser or some reference code together from the client and the server checks it matches additional data the JWT token was generated and stored with. (However, I read that the standard practice is not to store anything on the server.)

Please advise as I need to implement Java JWT (JJWT), RESTful Java Jersey and Google Web Toolkit. (I've been reading documentation such as this: [https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage]).

Thank you!

ikevin8me
  • 4,253
  • 5
  • 44
  • 84

2 Answers2

8

Possesion of a JWT is the proof of authentication. An attacker who stoles a token can impersonate the user.

So, keep tokens secure:

  • use a TLS channel
  • add extra security measures depending on the type of storage. Cookies are vulnerable to CSRF attacks. use HttpOnly if you do not need to access token from javascript. LocalStorage is vulnerable to XSS attacks
  • set short expiration time on authentication tokens and require credentials if token is expired

Blacklisting is not useful because you won`t know that a JWT has been stolen. And its usage breaks stateleness, one of the advantages of JWT

Additionally is possible to add the IP the token, but consider the usage scenario because it can be problematic on mobile devices or systems behind a proxy

pedrofb
  • 37,271
  • 5
  • 94
  • 142
  • Better answer than mine :-) – mxlse Jan 21 '17 at 21:03
  • 1
    You could also add some fingerprint of the browser (e.g. User-Agent and Accept-* headers). Also use 2 cookies, one of them being a SameSite cookie and required for all sensitive operations (csrf could still steal data, but not manipulate them) – Thomas Broyer Jan 22 '17 at 08:42
  • so a token generated by the server is digitally signed by the server and given to the client, such that when token comes back to the server then the server will recognize it that it was the token it generated. However, the server cannot be 100% sure that it is the same client. The server is only 100% sure that it generated the token. So as mentioned in other comments, there is a need at the client side (client ip address, client browser fingerprints, etc) to guarantee it is the same client but then, it breaks on certain use cases such as proxy, or mobile phones. Is my understanding correct? – daparic Feb 05 '18 at 18:03
  • 1
    it is correct @ifelsemonkey. When the JWT is issued, additional security means can be added according to the usagechannel, the device (browser, mobile, desktop) or infrastructure. There is no a magic solution... – pedrofb Feb 05 '18 at 20:42
2

On the client you are building the JWT like:

byte[] key = getSignatureKey();

String jwt = Jwts.builder().setIssuer("myTestApplication")
    .setSubject("myTest")
    .setExpiration(expirationDate)
    .put("scope", "testing") 
    .signWith(SignatureAlgorithm.HS256, key)
    .compact();

On the server side you can verify the JWT in regards to the the key and the expiration date exp (and more i.e. creation date, issuer iss, audience aud):

String subject = "notMyToken";
try {
    Jws jwtClaims = Jwts.parser().setSigningKey(key).parseClaimsJws(jwt);

    subject = claims.getBody().getSubject();

    //OK, we can trust this JWT
} catch (SignatureException e) {
    //don't trust the JWT!
}

Stealing the JWT should be avoided by using SSL,...but if the JWT is stolen there would be the risk of replaying exactly this JWT - right. That's where jti comes in.

The jti (JWT ID) claim provides a unique identifier for the JWT. The identifier value MUST be assigned in a manner that ensures that there is a negligible probability that the same value will be accidentally assigned to a different data object; if the application uses multiple issuers, collisions MUST be prevented among values produced by different issuers as well. The jti claim can be used to prevent the JWT from being replayed. The jti value is a case-sensitive string. Use of this claim is OPTIONAL.

With this identifier you could recognize if this ID was already sent (you have to blacklist it on the server side which somehow undermines the nature of JWT). Because you should use the expiration date you could clean the IDs if the expiration date leads to a SignatureException.

However, if the 'hacker' stole the JWT out of the database, as you wrote in the question, you have further problems beside the stolen JWT probably, because than the attacker could also steal other data etc.

Hope this helped a bit.

mxlse
  • 2,654
  • 17
  • 31