Stateless Spring Security Part 1: Stateless CSRF protection

Today with a RESTful architecture becoming more and more standard it might be worthwhile to spend some time rethinking your current security approaches. Within this small series of blog posts we’ll explore a few relatively new ways of solving web related security issues in a Stateless way. This first entry is about protecting your website against Cross-Site Request Forgery (CSRF).

Recap: What is Cross-Site Request Forgery?

CSRF attacks are based on lingering authentication cookies. After being logged in or otherwise identified as a unique visitor on a site, that site is likely to leave a cookie within the browser. Without explicitly logging out or otherwise removing this cookie, it is likely to remain valid for some time.

Another site can abuse this by having the browser make (Cross-Site) requests to the site under attack. For example including some Javascript to make a POST to “http://siteunderattack.com/changepassword?pw=hacked” will have the browser make that request, attaching any (authentication) cookies still active for that domain to the request!

Even though the Single-Origin Policy (SOP) does not allow the malicious site read access to any part of the response. As probably clear from the example above, the harm is already be done if the requested URL triggers any side-effects (state changes) in the background.


 Common approach

The commonly used solution would be to introduce the requirement of a so-called shared secret “CSRF-token” and make it known to the client as part of a previous response.
The client is then required to ping it back to the server for any requests with side-effects. This can be done either directly within a form as hidden field or as a custom HTTP header. Either way other sites cannot successfully produce requests with the correct CSRF-token included, because SOP prevents responses from the server from being read cross-site. The issue with this approach is that the server needs to remember the value of each CSRF-token for each user inside a session.

Stateless approaches

1. Switch to a full and properly designed JSON based REST API.

Single-Origin Policy only allows cross-site HEAD/GET and POSTs. POSTs may only be one of the following mime-types: application/x-www-form-urlencoded, multipart/form-data, or text/plain. Indeed no JSON! Now considering GETs should never ever trigger side-effects in any properly designed HTTP based API, this leaves it up to you to simply disallow any non-JSON POST/PUT/DELETEs and all is well. For a scenario with uploading files (multipart/form-data) explicit CSRF protection is still needed.

2. Check the HTTP Referer header.

The approach from above could be further refined by checking for the presence and content of a Referer header for scenarios that are still susceptible, such as multipart/form-data POSTs. This header is used by browsers to designate which exact page (url) triggered a request. This could easily be used to check against the expected domain for the site. Note that if opting for such a check you should never allow requests without the header present.

3. Client-side generated CSRF-tokens.

Have the clients generate and send the same unique secret value in both a Cookie and a custom HTTP header. Considering a website is only allowed to read/write a Cookie for its own domain, only the real site can send the same value in both headers. Using this approach all your server has to do is check if both values are equal, on a stateless per request basis!


Implementation

Focussing on the 3rd approach for explicit but Stateless CSRF-token based security, lets see how this looks like in code using Spring Boot and Spring Security.

Within Spring Boot you get some nice default security settings which you can fine tune using your own configuration adapter. In this case all that is needed is to disable the default csrf behavior and add our own StatelessCSRFFilter:

And here is the implementation of the StatelessCSRFFilter:

As expected the Stateless version doesn’t do much more than a simple equals() on both header values.

Client-side implementation

Client-side implementation is trivial as well, especially when using AngularJS. AngularJS already comes with build-in CSRF-token support. If you tell it what cookie to read from, it will automatically put and send its value into a custom header of your choosing. (The browser taking care of sending the cookie header itself.)

You can override AngularJS’s default names (XSRF instead of CSRF) for these as followed:

Furthermore if you want to generate a new token value per request you could add a custom interceptor to the $httpProvider as followed:

You can find a complete working example to play with at github.
Make sure you have gradle 2.0 installed and simply run it using “gradle build” followed by a “gradle run”. If you want to play with it in your IDE like Eclipse, go with “gradle eclipse” and just import and run it from within your IDE (no server needed).

Disclaimer:
Sometimes the classical CSRF-tokens are wrongfully deemed a solution against replay or brute-force attacks. The stateless approaches listed here does not cover this type of attack. Personally I feel both type of attacks should be covered at another level, such as using https and rate-limiting. Which I both consider a must for any data-entry on a public web site!

7 thoughts on “Stateless Spring Security Part 1: Stateless CSRF protection

  1. I have implemented your suggestions with some changes but when I try to do the login I get an error because AngularJS send a preflight request (OPTIONS) which produces the error:
    com.fasterxml.jackson.databind.JsonMappingException: No content to map due to end-of-input

    I tried setting to text/plain but unfortunately no luck. Would you have any suggestions?

  2. The OPTIONS request is the result of the browser making a CORS request. If you just trying to access the same domain, try checking if you indeed use relative urls. If you do mean to actually make a request to another domain, you should handle the OPTIONS request in a general CORS filter on the server-side to handle the preflight request, not bothering the application itself with it.

    Either way you should not try and solve this by changing logic in the server application itself.

  3. Hey,
    thanks for this excellent article, it really helped me wrap my mind around CSRF in a stateless environment and this is a quite elegant solution to the problem.
    However, we ran into some kind of race condition I guess, as have some other people, see for example this stackoverflow post:
    http://stackoverflow.com/questions/28508848/csrf-token-in-header-and-cookie-do-not-match-in-requests

    The problem being, that sometimes the data in header and cookie coming from the client does not match, though this is a local development environment without any csrf attacks occurring ;) do you have an idea what the problem could be?

    • That is probably because angularjs (version dependant) is (sometimes) reading your cookie before it’s being modified. Resulting in the custom header being the previous value.

      You might want to try a higher angularjs version or move the logic to the after phase of the interceptor or some other custom trigger.

  4. Hey Thanks for well written article but i have some confusion over my project i am building an web application which is completely REST based and i have no browser client like angular and i am preparing the backend for mobile app then your “stateles csrf token” solution would work or not as it checks header and cookies sent from client side. I am using REST client(POSTMAN) to check my rest services.