Home > Tech stuff > Adding CSRF security in Spring based Web application

Adding CSRF security in Spring based Web application

Cross-site request forgery, also known as a one-click attack or session riding and abbreviated as CSRF (pronounced sea-surf) or XSRF, is a type of malicious exploit of a website whereby unauthorized commands are transmitted from a user that the website trusts. Unlike cross-site scripting (XSS), which exploits the trust a user has for a particular site, CSRF exploits the trust that a site has in a user’s browser.

CSRF protection is intended to prevent state-altering requests that do not originate from the application itself. All non-idempotent actions should require a nonce generated by the server to accompany the request. This is to ensure the user intended to perform the action by requiring the source of the action to be provided by the server.

An easy way to implement this fix is to have every page rendered with a hidden variables.

On the backend, you take the variable and validate it. If valid, you can allow the request to continue. This allows you to set expiration on pages and authenticate their creation so cross site request forgery attacks are mitigated. It should not be possible to use the same valid token twice.

I had a Spring @Controller(s) exposing the non-idempotent PUT/POST methods.

1. When a new session is created, server sends a CSRF token to the UI. It will be unique per session. Check the postHandle method.

package com.mypkg.web.security;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class MySecurityHandlerInterceptor implements HandlerInterceptor {

  private static final Logger LOG = LoggerFactory.getLogger(MySecurityHandlerInterceptor.class);

  @Autowired
  MyCSRFTokenManager myCsrfTokenManager;

  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

    if ("POST".equalsIgnoreCase(request.getMethod()) || "PUT".equalsIgnoreCase(request.getMethod())
        || "DELETE".equalsIgnoreCase(request.getMethod())) {
      String sessionToken = myCsrfTokenManager.getTokenForSession(request.getSession());
      String requestToken = myCsrfTokenManager.getTokenFromRequest(request);
      if (sessionToken.equals(requestToken)) {
        return true;
      } else {
        LOG.error("Possible CSRF attack! " + request.getRequestURI());
        String requestURI = request.getRequestURI();
        if (requestURI.contains("ignoreURL")) {
          return true;
        }
        response.sendError(HttpServletResponse.SC_FORBIDDEN, "Bad Request");
        return false;
      }
    } else {
      // idempotent request. Pass through
      return true;
    }
  }

  @Override
  public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
      ModelAndView modelAndView) throws Exception {
     //Set the CSRF token in the session.
    if (request.getSession() != null) {
      request.setAttribute(MyCSRFTokenManager.MY_CSRF_TOKEN,
          myCsrfTokenManager.getTokenForSession(request.getSession()));
    }

    response.addHeader("X-FRAME-OPTIONS", "SAMEORIGIN");
  }

  @Override
  public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
      throws Exception {}

}

2. With each call UI will send back the CSRF token to the server
If you have a common JSP (which is included in all other JSPs), like header.jsp or title-bar.jsp, set an hidden variable to be passed through

    <input type="hidden" id="MY_CSRF_TOKEN" name="MY_CSRF_TOKEN" value="${MY_CSRF_TOKEN}"/>

For all ajax calls,

	//Adding a Token to mitigate CSRF attacks
	$.ajaxPrefilter(function (options, originalOptions) {
	    options.headers = $.extend(originalOptions.headers, { "MY_CSRF_TOKEN": $('#MY_CSRF_TOKEN').val() });
	});

3. MySecurityHandlerInterceptor preHandle intercepts the calls and validates the token sent by the UI. If it matches up, the call is allowed to pass through.

  1. No comments yet.
  1. No trackbacks yet.

Leave a comment