Happy New Year!! What better way to kick off this new year than to write a new post? Let’s get to it. This post will not try to show you best practices for protecting against CSRF (cross-site request forgery). Instead, I hope to start a discussion on what would be the best way to achieve CSRF protection in a Backbone app using the new Spring Security 3.2.0 release, so that I may better understand it myself. The source code for this experiment is available at the bottom of this post.
Be advised this post has been revised since it’s original writing. Rob Winch, Spring Security project lead suggested a simpler method of doing what I needed and indeed, it is simpler.
What does Spring Security CSRF protection assume?
In version 3.2.0, CSRF is enabled by default when using Spring Security. Since I’m building a Backbone app which will be accessed from a browser, I want the added protection. While reading the documentation, I quickly realized that the way the implementation has been done, the CSRF token needed on your page is stored in a session attribute on the HttpSession. On the backend, the CSRF token is created using a HttpSessionCsrfTokenRepository.
The token is created automatically when you request the page being protected. This doesn’t pose any problems if you’re building your app with JSP for example. If you’re using Spring MVC’s JSP tag libs, or something like Thymeleaf (which I loved using before discovering Backbone) then the CSRF token is available for you as a page attribute when the page is rendered on the server and can be inserted in a form for example or in a meta tag for later use in an AJAX request. In a Backbone app however, where the page is rendered primarily in the browser, you need another means to get that CSRF token when you first load the page because we don’t get access to page attributes like we do in JSP.
How about a servlet filter?
One thought I had to retrieve the CSRF token at page load was to create a servlet filter. This allows me to retrieve the CSRF token from the request and add information about it as response headers.
This filter is then used in my
configure(HttpSecurity http) method so that it occurs after Spring Security’s
Retrieving the CSRF token in our Backbone app
Here’s a snippet from my Backbone login view which does just that. The login view gets rendered when I first browse to my app unauthenticated.
The login view’s render method intercepts the login form submission, then submits an AJAX GET request to the login page again. Upon success, the
X-CSRF-TOKEN header is retrieved and it’s value is appended to the login form as a hidden input field. Then the form’s submission proceeds. If the AJAX call fails, I’m not currently doing anything special on the page, although I could display an error. For now I’m just logging the error to the browser console. Assuming everything worked and the user enters a valid username and password, then the page should be redirected to our application.
I’m hoping this raises some opinions or comments on the best way to accomplish what I’ve done. Of course if this is a good practice, maybe the Spring Security team will add this functionality. All they really need is 1 line added to the saveToken method of the
HttpSessionCsrfTokenRepository and then the need for a new filter would be negated. If this isn’t the best approach, I’d like to hear what anyone else thinks.
As for the Backbone app, there’s a few things I could have done extra or differently. Things like submitting the login via AJAX instead of redirecting the page after a successful login, also displaying errors in the browser if login fails.
The full source code for this post can be found here https://bitbucket.org/pgrimard/spring-security-csrf-backbone. You’ll need npm, bower and grunt installed to build the client side code as it was generated with a Yeoman generator. The full project uses Maven.
Feel free to post your comments below :)