user4786688
user4786688

Reputation:

Are JWTs stored in cookies safe?

I'm trying to make a JWT-based authentication process.

  1. Started Jersey REST service on Jetty
  2. The user posts with username and password at /users/login
  3. A JWT based on secret key is generated at the server and stored in a cookie
  4. The client can now read the cookie for other actions, send a request with Authorization: Bearer ${jwt} header to /users/profile
  5. If the jwt, by signature, is valid, the user information is returned, else respond with Unauthorized

Now, I just realized; how is this safe? Can't any user simply read the cookie from the browser and curl request to /users/profile and it will be valid?

I'm really still a newbie in this, so could you please explain? Here's some simple test code

@Path("users")
public class UserResources {
    @Path("login")
    @POST
    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response loginUser(UserInput userInput) {
        String compactJws = Jwts.builder().setSubject(userInput.getUsername()).claim("name", userInput.getName())
                .signWith(SignatureAlgorithm.HS256, Server.SECRECT_KEY).compact();

        return Response.status(Status.OK).cookie(new NewCookie("jwt", compactJws)).entity(compactJws).build();
    }

    @Path("profile")
    @GET
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response getUser(@HeaderParam("Authorization") String compactJws) {
        try {
            if (compactJws == null || compactJws.trim().isEmpty() || !compactJws.startsWith("Bearer ")) {
                throw new SignatureException("Invalid access token");
            }
            Jws<Claims> claims = Jwts.parser().setSigningKey(Server.SECRECT_KEY)
                    .parseClaimsJws(compactJws.substring("Bearer ".length()));
            return Response.ok(new String("Subject: " + claims.getBody().getSubject() + "\nName: " + claims.getBody().get("name"))).build();
        } catch (SignatureException e) {
            return Response.status(Status.UNAUTHORIZED).entity(e.getMessage()).build();
        }
    }
}

Upvotes: 9

Views: 1469

Answers (1)

Nate Barbettini
Nate Barbettini

Reputation: 53600

Validating the signature guarantees that the JWT hasn't been forged or tampered with. As you guessed, the JWT can still be stolen. If someone steals the token, they can make impersonate your user and make requests.

There are three important things to do:

  1. Use HTTPS - end-to-end TLS prevents someone intercepting or sniffing the requests on the wire and stealing the token.

  2. Store tokens securely on the client - If you store your token in a cookie, make sure to use the HttpOnly flag to prevent theft via XSS, and use CSRF protection on all your forms. See Cookies vs. HTML5 Web Storage for a deeper explanation.

  3. Expire the tokens - if all else fails, and a token does get stolen, a sensible expiration (exp claim) will ensure that there's only a limited window of time that the token can be used to cause damage.

Upvotes: 10

Related Questions