2

I'm building a site with Spring, which requires authentication in order for accessing certain pages.

The site is actually composed of a client host, which render the views and handle the data binding, and a REST api host (built with Spring as well), it's responsibility is returning/manipulating data, validation/generation of user token etc..

I've decided to implement a custom authentication in the REST side, upon receiving a valid username and password, it will return:

  • JWT signed by the REST API with AES-256, the payload will contain the user id and expiration date.
  • The JWT is stored on a cookie, which is HTTP only, secure, and valid only from the client site domain (is it enough to counter measure csrf attacks?)

The only backdoor I could think of is if the attacker steals the cookie, for which I could add to the JWT payload a unique random value for the user (which is stored on the database), and in the case the user is suspecting that his session was stolen, he could issue a request to reset the unique value, rendering the stolen token useless.

Am I missing any backdoors which could allow an attacker to bypass the authentication easily? Should I use Spring Security instead? I'm afraid I will not be able to achieve the stateless behaviour I currently have...

Thanks!

Nadav96
  • 193
  • 6

1 Answers1

3

You will be vulnerable to a CSRF attack.

Since you want to be stateless, best option is to use double submit cookie variation. You need to be careful and not to do it naively, please see this question. In short, you will need to tie your CSRF token to a security session:

  1. Generate sufficiently large CSRF token
  2. Include CSRF token inside JWT as one of the claims
  3. Provide CSRF to the client as a result of a successful authentication in a JSON object for example. You will provide cookie containing your JWT in the same step. Cookie can't be read from the client (httpOnly) thus the necessity to provide it in a JSON object or some other format.
  4. On client side store CSRF token in sessionStorage or localStorage and send it to the server on each request that changes state as a header.
  5. On server side validate the value from header and JWT

Your solution for revoking JWT is fine. Be sure to generate new id and issue new token when user changes password.

To minimize possible impact a stolen JWT can have on your application, keep expiration as short as your application can allow it to be.


You can implement a stateless JWT security using Spring Security and should avoid doing it in a REST controller and rolling your own custom implementation. See this as a good starting point. Even though it is for Spring Boot, it can easily be modified so it works with plain Spring.

Marko Vodopija
  • 1,062
  • 1
  • 8
  • 19
  • I can't thank you enough for setting me up with this article! I've successfully intergated Spring Security while keeping the jwt token authentication in the app :) – Nadav96 Apr 01 '17 at 16:01
  • And regarding the double submit cookie, I've built a simple filter that does that (and it's working from what I see), is it wrong to build one myself? Should I use prbuilt? The code can be [found here](https://github.com/nadav96/AntiCSRF-Spring-Security-Filter/tree/master/src/main/java/com/csrf/anti/security/csrf). Marking this answer as correct regardless. – Nadav96 Apr 01 '17 at 16:03
  • 1
    Yes, you need to implement your own filter since you are storing csrf token inside JWT and standard solution will not know how to handle it. By looking at the code I see you did a standard double submit. You really nee to tie the csrf to jwt. This means instead of separate cookie, you use jwt cookie used for authentication. Please take a look at linked question and a blackhat article inside it. It will explain why. – Marko Vodopija Apr 01 '17 at 18:33
  • Thank you very much for the suggestion, I decided to keep the seperation between the session cookie (which is HttpOnly), and the csrf cookie, **BUT** I added a claim in the jwt token that contains a random string, which will serve as the csrf value, and upon receiving request, it will first verify the jwt token intergiy, then proceed to check that the jwt claim is equal to the csrf cookie and the http header value. How does that sound? Are there more repercussions that I can take? I'm generating the csrf value using `UUID.randomUUID().toString()` , Is that good enough? Can it be brute forced? – Nadav96 Apr 02 '17 at 18:21
  • It is fine approach but your csrf cookie is reduntant. As long as your are comparing it with a value stored in jwt, it will be fine though. UUID.randomUUID() is enough. It has 122bit entropy and uses CSPRNG. – Marko Vodopija Apr 03 '17 at 05:49
  • Why reduntant? If I only rely on the cookie that stores the jwt token, I have to disable httpOnly (so the javascript can read and set the corrosponding header), hence making it more vulnerable to xss (can be stolen), isn't seperating a good idea? – Nadav96 Apr 04 '17 at 19:51
  • You could provide CSRF token in a body of response when you do initial login. On client side you could save that token inside sessionStorage. That way you don't need a csrf cookie. Later, on backend when verifying, you compare a value in JWT and value you get in a header. Regardless, it is very important that you don't compare header with cookie but header with the value inside JWT. If you don't do that, an attacker could write his own cookie which you would use to compare the values and thus defeating CSRF protection. Writing cookies is easier than reading them. See linked question. – Marko Vodopija Apr 05 '17 at 09:15