ikevin8me
ikevin8me

Reputation: 4313

JSON Web Token (JWT)

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!

Upvotes: 4

Views: 1901

Answers (2)

pedrofb
pedrofb

Reputation: 39311

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

Upvotes: 8

mxlse
mxlse

Reputation: 2784

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.

Upvotes: 2

Related Questions