New in Spring MVC 3.1: CSRF Protection using RequestDataValueProcessor
Introduction
As a software architect one of the common tasks I have to deal with is web applications security. Usually I would try to make sure that security is automatically enforced by the infrastructure, however this is not always that easy – sometimes the underlying frameworks don’t provide any built in support or configuration which globally turns on a security attribute. This is why the new org.springframework.web.servlet.support.RequestDataValueProcessor interface in Spring MVC 3.1 seems to be very interesting: it provides a clean way to implement automatic CSRF protection.(BTW – the lately released 'Pro Spring 3'book covers some of Spring 3.1 new features – such as profiles – but not the one discussed here).
CSRF - What Is It?
Cross-site request forgery (CSRF) is one of the most common web applications vulnerabilities (ranked number 5 on OWASP's Top 10 document). The following section, taken from Wikipedia, explains CSRF severity: "According to the United States Department of Homeland Security the most dangerous CSRF vulnerability ranks in at the 909th most dangerous software bug ever found, making this vulnerability more dangerous than most buffer overflows. Other severity metrics have been issued for CSRF vulnerabilities that result in remote code execution with root privileges as well as a vulnerability that can compromise a root certificate, which will completely undermine a public key infrastructure." (here)Both links above include detailed information about CSRF attacks but it can be summarized in one short sentence: the core weakness the CSRF attacker is taking advantage of are predictable URLs and request bodies which changes the application state. As an example let’s assume a banking web application in which the 'wire money' form includes two fields: the amount to wire and the destination account number. An attacker can send an email to users which points to a page that silently posts a wire transaction request to the bank systems. If a user clicks that link, while his browser holds a valid session with the bank web site (for example in another tab), the wire transaction will be accepted by the bank systems. On the other hand if the wire form includes an unpredictable value (a secret) which is validated on submit the attack would fail. To mitigate CSRF attacks any request that changes the application state has to include an unpredictable secret token which must be validated before processing the request.
Before proceeding to the implementation details few things to notice:
- I assume (and this is the way it should be!) that only POST requests change the application state
- When discussing CSRF we often hear a sentence like: "but the attacker can use JavaScript to read your form structure and understand what the secret token is – so this CSRF token is actually useless". In practice the browser's same origin policy makes it very difficult for the attacker to read the CSRF token using JavaScript originated from his site.
Implementation Overview
I would like include a session private CSRF token in any form rendered to the UI and to enforce the existence and validity of that token on each POST request arriving to the application – so we basically have two components: out-bound form enrichment and in-bound request validation. The solution is fully automatic: once configured into the application all forms and POST requests will be CSRF secured without the need for any explicit action to be taken by application developers. In my solution the CSRF token will be HTTP session scoped - each session will have its own CSRF token valid to the entire session.CSRFTokenManager
This is a utility class, used by both the in-bound and out-bound components. The class is responsible for managing the CSRF token for HTTP sessions. The key method in the class is getTokenForSession illustrated below (the full class source is on github - in this link):static String getTokenForSession (HttpSession session) { String token = null; // I cannot allow more than one token on a session - in the case of two requests trying to // init the token concurrently. // Notice: in real life I wouldn't synchronize on the session instance. // This should be done on an attribute on the session. But for the // blog demo this is fine synchronized (session) { token = (String) session.getAttribute(CSRF_TOKEN_FOR_SESSION_ATTR_NAME); if (null==token) { token=UUID.randomUUID().toString(); session.setAttribute(CSRF_TOKEN_FOR_SESSION_ATTR_NAME, token); } } return token; }
The getTokenForSession method checks for the existence of a CSRF token as an attribute on an HTTP session, if one exists it returns its value otherwise it generates the session token, store it on the session and returns the token value to the caller. The method must synchronize on the session otherwise we might end with a caller getting a token which is no longer valid for the session (if more than one request trying to access the method concurrently and a token was not generated for the session yet). In my usage the token is a random GUID but any other random value is valid.
Form Rendering (out-bound)
I have to make sure that any form rendered using my Spring MVC based application will include the CSRF token as a hidden field. I do this by implementing the getExtraHiddenFields() method of the org.springframework.web.servlet.support.RequestDataValueProcessor interface (remember since Spring 3.1). By implementing this method my class gets the opportunity to add hidden fields to any form rendered using Spring's form tag (<form:form....), obviously I will add a field with the CSRF token. Below is my implementation of the method (full class code on github):public class CSRFRequestDataValueProcessor implements RequestDataValueProcessor {
...
...
@Override
public Map<String,String> getExtraHiddenFields(HttpServletRequest request) {
Map<String,String> hiddenFields = new HashMap<String,String>();
hiddenFields.put(CSRFTokenManager.CSRF_PARAM_NAME,
CSRFTokenManager.getTokenForSession(request.getSession()));
return hiddenFields;
}
}
Still not done, for the processor to be invoked by Spring it has to be registered to Spring's RequestContext, the easiest way of doing that is to register an instance of my CSRFRequestDataValueProcessor as bean named 'requestDataValueProcessor' in the Bean Factory:
<!-- Data Value Processor --> <bean name="requestDataValueProcessor" class="com.eyallupu.blog.springmvc.controller.csrf.CSRFRequestDataValueProcessor"/>
Enforcing CSRF Token Validity for Incoming POST Request (in-bound)
The last part is to make sure that each incoming POST request includes a valid CSRF token for the session to which the request belongs. Usually the first approach JEE developers would adopt is to use a Servlet filter which checks to see if the current request is a POST one and if so it validates the existence of the CSRF token and its content. The issue with that approach is the fact that the Servlet filter processing takes place before the request is routed to Spring's DispatcherServlet. In a multipart encoded forms (multipart/form-data) use case this would be proven wrong: since Spring has its own strategy to process multipart requests (look for MultipartResolver and MultipartHttpServletRequest in Spring's source) processing the request before Spring does will collide with Spring.A more 'Spring like' way of doing so is using a HandlerInterceptor. Spring handler interceptors can be registered to add common pre or post processing to controllers. Unlike the Servlet filter those interceptors are a part of the Spring MVC request life cycle and it is fully synchronized with both multipart and simple (application/x-www-form-urlencoded) forms. Here is my relevant interceptor code (on github):
public class CSRFHandlerInterceptor extends HandlerInterceptorAdapter { ... @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (!request.getMethod().equalsIgnoreCase("POST") ) { // Not a POST - allow the request return true; } else { // This is a POST request - need to check the CSRF token String sessionToken = CSRFTokenManager.getTokenForSession(request.getSession()); String requestToken = CSRFTokenManager.getTokenFromRequest(request); if (sessionToken.equals(requestToken)) { return true; } else { response.sendError(HttpServletResponse.SC_FORBIDDEN, "Bad or missing CSRF value"); return false; } } }
The last step is to register the interceptor into Spring's processing chain:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" ... ... ... <!-- Interceptor handlers --> <mvc:interceptors> <bean class="com.eyallupu.blog.springmvc.controller.csrf.CSRFHandlerInterceptor"/> </mvc:interceptors> </beans>
Comments
Do you have a unit test for testing the unique tokens?
For AJAX – I register with each ajax request a filter (if I remember correctly using: $.ajaxPrefilter() ) which pushes the CSRF to each post request's data.
Will this solution work for multiple forms in the same page ?
$.ajax({
...
beforeSend: function ( xhr ) {
xhr.setRequestHeader('CSRFToken', csrf_token);
}
}
very interesting post. Just a quick question, in addition to the forms accessible from the browser I also use a command line client that I use to post the information.
The CSRF protection is detecting the client as invalid. Any ideas about how I can apply it?
Thanks!
If the field (parameter) is posted from the command line client it should work - it might a post issue that make things hard for Spring to extract the value.
I would start with curl - trying to make it work and then make sure that my command line is working the same way.
Also remember that each session has its own token - did you initiated a session with the command line client and maintained it (sending the JSESSIONID cookie)?
Eyal
we SiteMinder for user authentication, so the session is created there. I was only managing the SM cookie, I will also add the JSESSIONID cookie and see.
We have our application in Spring 3.1.2 MVC portlet. Can we also implement this solution to prevent CSRF attacks? I am in doubt whether this will work for SPRING MVC Portlet or not?
Thanks,
Vaibhav
I am not familiar with spring port lets so I wouldn't know to answer that...
Sorry
Eyal
Thanks,
Vaibhav
Please help me.........
Thank you for the informative article.
To finally clarify, it is possible to store the token in the cookie. This is referred to as the "Double Submit Cookies" pattern. The Django framework uses this pattern.
So I have taken the approach of adding the hidden field manually to each of my POST forms (using ${sessionScope['CSRFTokenManager.tokenVal']}). If someone has a better way, please let me know.
$.ajax({
...
beforeSend: function ( xhr ) {
xhr.setRequestHeader('CSRFToken', csrf_token);
}
}
Where does "csrf_token" come from? I want to have this evaluated to the token from the server side as in your examples but I have no idea how to access it from AJAX/my page. Even going with the AJAX PreFilter() route, I am not sure how exactly to get the token in the same way. Sorry for the noobish question...
<form:form action=null it is showing in page view scource
EMTV is Papua New Guinea premier television station and has been the country window to the world for over two decades.It is Owned by Fiji television.
PNG Sports News
Thanks,
melina
http://www.thinkittraining.in/java-script-and--jquery
visit us for more details http://www.thinkittraining.in/java-training
About more knowledge http://www.thinkittraining.in/java-training
Thanks so much for the blog post.............
I am actually delighted to glance at this webpage posts which includes tons of useful data, thanks for providing such information.......
This is probably coming pretty late to the party but there's something I faced that makes me ask you this question. The "requestDataValueProcessor" that is injected in my context config, doesn't get called, which is why, the tokens are not populated on the rendered pages. This post (https://github.com/spring-projects/spring-boot/issues/3076) says there's one bean created by Spring security as well and that the Spring framework expects just 1 instance of the RequestDataValueProcessor to exist, which is why it's not picking the custom one. Spring's solution is to upgrade to 4.x where it has been fixed. Just wanted to know before making the switch how you managed to handle the issue at your end, using 3.1 itself.
Cheers,
Anirban
This is probably coming pretty late to the party but there's something I faced that makes me ask you this question. The "requestDataValueProcessor" that is injected in my context config, doesn't get called, which is why, the tokens are not populated on the rendered pages. This post (https://github.com/spring-projects/spring-boot/issues/3076) says there's one bean created by Spring security as well and that the Spring framework expects just 1 instance of the RequestDataValueProcessor to exist, which is why it's not picking the custom one. Spring's solution is to upgrade to 4.x where it has been fixed. Just wanted to know before making the switch how you managed to handle the issue at your end, using 3.1 itself.
Cheers,
Anirban
I am using this approach to implement csrf protection in an web application.the problem is after setting up the configuration the action tag in jsp is set to null. I am not sure what i have missed.
I am using the taglibs also.
Did anybody face this problem.
We have a legacy application where we still use Spring 2.5 based Spring MVC.But CSRFRequestDataValueProcessor is not available in Spring 2.5 any way. How to create that class on my own?
Can you please guide me here?
Can't thank you enough.
We have upgraded Spring version to 5.3. As per the upgrade "Cross-site request forgery" will be taken care automatically. But still coverity report is listing the issue.
Please provide help.