Creating a CSRF protection with Spring 3.1

January 11, 2012 by Michael

Note: This tutorial is for Spring Security 3.1, an updated version that uses the build-in CSRF protection of Spring Security 3.2 can be found here

CSRF Attacks still seems to be a problem, a pity that there is no standard solution in the Spring 3.1 framework. Although not probably, i wanted to protect my projects by malicious crafted links.

I didn’t want to use an extra library but something which is already available in the Spring framework. Here is what i come up with:

I choose the token protection mechanism for my implementation.

The core of my solution is the CSRFToken Service:

public interface CSRFTokenService {
	public final static String TOKEN_PARAMETER_NAME = "_tk";
 
	public final static String TOKEN_ATTRIBUTE_NAME = "CSRFToken";
 
	public final static List<String> METHODS_TO_CHECK = Collections.unmodifiableList(Arrays.asList("POST", "PUT", "DELETE"));
 
	/** Generates a new CSRF Protection token */
	public String generateToken();
 
	/** Obtains the token from the session. If there is no token, a new one will be generated */
	public String getTokenFromSession(final HttpServletRequest request);
 
	/** This method tests, if a token is acceptable when a user is logged in */
	public boolean acceptsTokenIn(HttpServletRequest request);
}
import java.security.SecureRandom;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
 
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
 
import de.dailyfratze.services.CSRFTokenService;
 
@Service("csrfTokenService")
public class CSRFTokenServiceImpl implements CSRFTokenService {
	private final SecureRandom random = new SecureRandom();
 
	@Override
	public String generateToken() {
		final byte[] bytes = new byte[32];
		random.nextBytes(bytes);
		return Base64.encodeBase64URLSafeString(bytes);
	}
 
	@Override
	public String getTokenFromSession(final HttpServletRequest request) {
		return request.getUserPrincipal() == null ? null : this.getTokenFromSessionImpl(request.getSession(false));
	}
 
	private String getTokenFromSessionImpl(final HttpSession session) {
		String token = null;
 
		if(session != null) {
			token = (String) session.getAttribute(TOKEN_ATTRIBUTE_NAME);
			if(StringUtils.isBlank(token))
				session.setAttribute(TOKEN_ATTRIBUTE_NAME, (token = generateToken()));
		}
		return token;
	}
 
	@Override
	public boolean acceptsTokenIn(HttpServletRequest request) {
		boolean rv = false;
 
		// Token is only verified if principal is not null
		if(request.getUserPrincipal() == null) 
			rv = true;
		else {
			final HttpSession session = request.getSession(false);
			rv = session != null && this.getTokenFromSessionImpl(session).equals(request.getParameter(TOKEN_PARAMETER_NAME));
		}
		return rv;
	}
}

“getTokenFromSession” is called right after a user logs in, so that the token gets stored into his session.

As you can see in the implementation of “acceptsTokenIn”, the token is only needed and verified when the principal is not null, meaning when a user is authenticated.

The interface contains some constants: The name of the token in forms and requests and the name of the attribute under which the token is stored in the session. The token itself is just a base64 of some random bytes.

I only want the token to be checked in writing methods: METHODS_TO_CHECK, meaning only in put, delete and posts requests. My applications don’t change state based on get requests.

So where to check for the token? I use a pretty simple Spring “HandlerInterceptor”:

package de.dailyfratze.controller;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
 
import de.dailyfratze.services.CSRFTokenService;
 
public class CSRFInterceptor implements HandlerInterceptor { 
	@Autowired
	private CSRFTokenService csrfTokenService;
 
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		boolean rv = true;
		if(CSRFTokenService.METHODS_TO_CHECK.contains(StringUtils.defaultIfBlank(request.getMethod(), "").toUpperCase()) && !csrfTokenService.acceptsTokenIn(request)) {
			response.addHeader("X-DailyFratze-InvalidCSRFToken", Boolean.toString(true));
			response.sendError(HttpServletResponse.SC_FORBIDDEN);
			rv = false;
		} 		
		return rv;
	}
 
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {	
	}
 
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {		
	}		
}

This interceptor stops the chain if the request method should be checked and the token is not acceptable by sending a HTTP forbidden error. The additional response header is used by Ajax calls to present a dialog that the session is invalidated.

How to get the token into forms? I wanted to be able to change the token name in only one place so i came up with the following custom tag:

import java.io.IOException;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
 
import org.apache.commons.lang.StringUtils;
 
import de.dailyfratze.services.CSRFTokenService;
import de.dailyfratze.utils.HelperRegistry;
 
/**
 * Creates a hidden input field with the CSRF Token
 * @author michael.simons, 2011-09-20
 */
public class CSRFTokenTag extends TagSupport {
	private static final long serialVersionUID = 745177955805541350L;
 
	private boolean plainToken = false;
 
	@Override
	public int doStartTag() throws JspException {
		final CSRFTokenService csrfTokenService = HelperRegistry.getHelper(super.pageContext.getServletContext(), super.pageContext.getRequest(), CSRFTokenService.class, "csrfTokenService");
		final String token = csrfTokenService.getTokenFromSession((HttpServletRequest) super.pageContext.getRequest());
		if(!StringUtils.isBlank(token))
			try {
				if(plainToken)
					pageContext.getOut().write(token);
				else
					pageContext.getOut().write(String.format("<input type=\"hidden\" name=\"%1$s\" id=\"%1$s\" value=\"%2$s\" />", CSRFTokenService.TOKEN_PARAMETER_NAME, token));
			} catch (IOException e) {
			}
		return SKIP_BODY;
	}
 
	@Override
	public int doEndTag() throws JspException {
		return EVAL_PAGE;
	}
 
	public boolean isPlainToken() {
		return plainToken;
	}
 
	public void setPlainToken(boolean plainToken) {
		this.plainToken = plainToken;
	}
 
	public static String getTokenParameterName() {
		return CSRFTokenService.TOKEN_PARAMETER_NAME;
	}
}

with the corresponding mapping:

<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
	version="2.1">
	<tlib-version>1.0</tlib-version>
	<short-name>df</short-name>
	<uri>http://michael-simons.eu/taglibs/df</uri>
	<tag>
		<name>csrfToken</name>
		<tag-class>de.dailyfratze.tags.CSRFTokenTag</tag-class>
		<body-content>empty</body-content>
		<attribute>
			<name>plainToken</name>
			<required>false</required>	
		</attribute>
	</tag>	
	<function>
		<name>csrfTokenParameter</name>
		<function-class>de.dailyfratze.tags.CSRFTokenTag</function-class>
		<function-signature>java.lang.String getTokenParameterName()</function-signature>
	</function>
</taglib>

I can use this tag in forms like so:

<form method="post" action="foobar">
   <df:csrfToken />
</form>

Or for generating url parameters for example for ajax calls like so:

<c:url value="/foobar">
  <c:param name="${df:csrfTokenParameter()}">
    <df:csrfToken plainToken="true"/>
  </c:param>
</c:url>

So if a token is invalid, the user is either redirect to an error page if it is a normal post, ajax calls through jQuery can be handled like so:

function isInvalidCSRFToken = function(xhr) {
	var rv = false;
	if(xhr.status == 403 && xhr.getResponseHeader('X-DailyFratze-InvalidCSRFToken') == 'true') {			
		alert($('Session is invalid').text());
		rv = true;
	}
	return rv;
}
 
$.ajax({
	type: 'post',
	url: theUrl,	
	dataType: 'text',		
	complete: function(xhr, status) {
		if(isInvalidCSRFToken(xhr))	    				
			return;	    				
		// handle the result
	} 	        
});

The code snippets are all taken from a running project. If you want to use them, use them. The package names are missing and must be added. Also the JavaScript code isn’t complete.

Feel free to comment, if you have suggestions, remarks or anything else. Also, if you can use this, i’d be happy to hear from you.

13 comments

  1. Sharath Kumar wrote:

    Please provide missing helperRegistry Class or jar and the missing Java sript.

    Posted on August 10, 2012 at 1:58 PM | Permalink
  2. Michael wrote:

    Hi Sharath,

    the “Registry” class is nothing fancy, just stuff to get to a Spring Singleton, have a look:

    public static <T> T getHelper(final ServletContext servletContext, ServletRequest servletRequest, final Class<T> clazz, final String name) {
    		final WebApplicationContext wc = RequestContextUtils.getWebApplicationContext(servletRequest, servletContext);
    		T rv = null;
    		if(wc != null) {
    			if(ObjectUtils.isBlank(name))
    				rv = wc.getBean(clazz);
    			else
    				rv = wc.getBean(name, clazz);
    		}
    		return rv;
    	}

    The first fragment of the JavaScript Code “isInvalidCSRFToken” is a complete function definition.

    The 2nd fragment however is just an example to demonstrate the usage of isInvalidCSRFToken in a jQuery AJAX. If the Ajax call returns than isInvalidCSRFToken checks if an error happened due to an invalid csrf token.

    Posted on August 21, 2012 at 2:31 PM | Permalink
  3. User wrote:

    Hi Michael,

    Thanks for posting a helful and wonderful post. It really helps to me.
    There is another way of doing it the same not at the application level but at the server level like in Tomcat we can achive the same by implementing it Tomcat Custom Valve class. I used the same approach. I extended tomcat ValveBase class and implement the same functionality. So it makes sure that before it reached the application Context, you token will be set/verified.

    Thanks
    User.

    Posted on March 7, 2013 at 7:39 PM | Permalink
  4. Michael wrote:

    Hi,
    thank you very much for your comment.

    That’s indeed a pretty neat idea doing it at the server level.

    On reason for me doing it in a Spring interceptor is that i want to exclude some handler (controller) in the preHandle, for example an api endpoint.

    Have a nice day,
    Michael.

    Posted on March 7, 2013 at 7:51 PM | Permalink
  5. keth wrote:

    hi, great post
    but can you please tell me how I’ll use CSRFInterceptor class .
    will i have to call its methods explicitly. please help !!

    Posted on April 8, 2013 at 11:19 AM | Permalink
  6. Michael wrote:

    Hi Keth,

    just configure it as a Spring Bean, either through xml or @Configuration. Spring will automatically notice it’s interceptor nature.

    Posted on April 8, 2013 at 11:29 AM | Permalink
  7. keth wrote:

    Hi Michael,

    I have implemented it in my code and everything is properly running, thanks for such a prompt reply and such a nice post .
    just wanted to ask how should i test my application manually and thoroughly for CSRF attack with this code implemented. so that i get assured that its prevented from CSRF attack.
    please help

    Posted on April 9, 2013 at 8:25 AM | Permalink
  8. Michael wrote:

    Hi Keth,
    thank you for your kind comments. Feel free to recommend my blog.

    Regarding your question:

    As you see in CSRFTokenService#acceptsTokenIn the interceptor here only works if a user is logged in (as it was in my use case, but you can change that). What cannot be changed is the part with the session, the CSRF token must be stored somewhere.

    In any case, be sure to call getTokenFromSession right after a user logs in or just in every request so that a token gets added to the session.

    So, log into your application to test it.

    Than choose a either a “POST”, “PUT” or “DELETE” action.

    If you haven’t adapted your forms and added a tag, your request should fail with a “403” forbidden.

    If you have added the tag to your forms, have a look at the source code via firebug, webinspektor or the like. There you’ll find a hidden input with the name “_tk” if you haven’t change the name in the tokenservice. Change it’s value to something else and your request will fail with a 403 if everything is into place.

    To sum it up:

    * a user logs in, a unique random token is generated and stored into his session
    * a token tag will be added (manually) to every form or action link that needs protection
    * post, put, delete methods will be affected
    * if those are invoked, the interceptor kicks in and checks the token parameter against the token in the session. if the match, everything is ok, if not, the request fails
    * so if an attacker forges a malicious input he cannot know the right token and a request will fail if the attacker manages to trick the user in to confirming the request.

    Hope this helps.
    Michael.

    Posted on April 9, 2013 at 9:14 AM | Permalink
  9. Parth Arora wrote:

    Excellent article. Helped me a lot. Thanks much.!

    Posted on November 11, 2013 at 2:01 PM | Permalink
  10. Michael wrote:

    Hi,
    thank you for your feedback, you’re welcome.

    Posted on November 11, 2013 at 2:04 PM | Permalink
  11. Michael wrote:

    In case anyone subscribed to this entry, here’s an update to use with Spring 3.2:

    http://info.michael-simons.eu/.....revisited/

    Posted on January 29, 2014 at 10:26 AM | Permalink
  12. Shakil wrote:

    how and where to put the xml code?

    Posted on December 7, 2015 at 8:07 AM | Permalink
  13. Michael wrote:

    Shakil: Better nowhere. Spring from 3.2 on contains an CSRF protection already, using this ideas.

    Posted on December 8, 2015 at 9:20 AM | Permalink
2 Trackbacks/Pingbacks
  1. #WJAX 2012 | info.michael-simons.eu on November 8, 2012 at 10:07 PM

    […] always interested in talks about security and also trying to improve stuff so Mikes talk was very […]

  2. […] Nearly two years earlier i wrote my CSRF protection implementation with Spring Security 3.1, have a look here. […]

Post a Comment

Your email is never published nor shared. Required fields are marked *