Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Security 3.2 CSRF support for multipart requests

Tags:

We have been using Spring Security with our application for a few years now. Last week we upgraded Spring Security from version 3.1.4 to 3.2.0. The upgrade went fine and we did not find any errors post the upgrade.

While looking through the Spring Security 3.2.0 documentation we came across the newly added features around CSRF protection and security headers. We followed the instructions in the Spring Security 3.2.0 documentation to enable CSRF protection for our protected resources. It works fine for regular forms but does not work for multipart forms in our application. On form submission, CsrfFilter throws an Access Denied error citing the absence of a CSRF token in the request (determined through DEBUG logs). We have tried using the first option suggested in the Spring Security documentation for making CSRF protection work with multipart forms. We do not want to use the second suggested option as it leaks CSRF tokens through the URLs and poses a security risk.

The relevant part of our configuration based on the documentation is available as a Gist on Github. We are using Spring version 4.0.0.

Note that we have already tried the following variations without success:

  1. Not declaring the MultipartFilter in web.xml.
  2. Not setting the resolver bean name for the MultipartFilter in web.xml.
  3. Using the default resolver bean name filterMultipartResolver in webContext.xml.

UPDATE: I have confirmed that the documented behaviour does not work even with a single page sample app. Can anyone confirm that the documented behaviour works as expected? Is there an example working application that can be used?

like image 339
manish Avatar asked Jan 28 '14 05:01

manish


People also ask

Does Spring handle multipart request?

By default, Spring does no multipart handling, because some developers want to handle multiparts themselves. You enable Spring multipart handling by adding a multipart resolver to the web application's context. Each request is inspected to see if it contains a multipart.

Is CSRF enabled by default in Spring Security?

As of Spring Security 4.0, CSRF protection is enabled by default with XML configuration. If you would like to disable CSRF protection, the corresponding XML configuration can be seen below. CSRF protection is enabled by default with Java Configuration.

How does CSRF work in Spring Security?

CSRF is an attack which tricks customer to submit a malicious request. With the help of CSRF, it let attackers hijacks the identity and let them perform unauthorized work on behalf of the user.


3 Answers

I was able to resolve this with help from the Spring Security team. I have updated the Gist to reflect a working configuration. I had to follow the steps given below in order to get everything to work as expected.


1. Common Step

Add a MultipartFilter to web.xml as described in the answer by @holmis83, ensuring that it is added before the Spring Security configuration:

<filter>
    <display-name>springMultipartFilter</display-name>
    <filter-name>springMultipartFilter</filter-name>
    <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>springMultipartFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<filter>
    <display-name>springSecurityFilterChain</display-name>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>ERROR</dispatcher>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>

2.1. Using Apache Commons Multipart Resolver

Ensure that there is an Apache Commons Multipart Resolver bean named filterMultipartResolver in the root Spring application context. I will stress this again, make sure that the Multipart Resolver is declared in the root Spring Context (usually called applicationContext.xml). For example,

web.xml

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        classpath*:springWebMultipartContext.xml
    </param-value>
</context-param>

springWebMultipartContext.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="filterMultipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="100000000" />
    </bean>
</beans>

Make sure that the bean is called filterMultipartResolver as any other bean name is not picked up by MultipartFilter configured in web.xml. My initial configuration was not working because this bean was named multipartResolver. I even tried passing the bean name to MultipartFilter using web.xml init-param but that did not work either.

2.2. Using Tomcat Multipart support

Tomcat 7.0+ has in-built multipart support, but it has to be explicitly enabled. Either change the global Tomcat context.xml file as follows or include a local context.xml file in your WAR file for this support to work without making any other changes to your application.

<Context allowCasualMultipartParsing="true">
    ...
</Context>

After these changes using Apache Commons Multipart Resolver our application is working so far on Tomcat, Jetty and Weblogic.

like image 106
manish Avatar answered Oct 04 '22 16:10

manish


This part:

<filter-mapping>
    <filter-name>multipartFilter</filter-name>
    <servlet-name>/*</servlet-name>
</filter-mapping>

Should be:

<filter-mapping>
    <filter-name>multipartFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

It is an error in Spring Security 3.2.0 documentation. The bug has been reported and will be fixed in upcoming version.

like image 30
holmis83 Avatar answered Oct 04 '22 17:10

holmis83


After struggling with this issue a bit, I found a much easier solution by just using the Request Header defined in Spring Security instead of trying to get the CSRF token embedded as a part of the multipart content.

Here is a simple way I setup the header using an AJAX library for file upload in my jsp:

var uploader = new AjaxUpload({
        url: '/file/upload',
        name: 'uploadfile',
        multipart: true,
        customHeaders: { '${_csrf.headerName}': '${_csrf.token}' },
        ...
        onComplete: function(filename, response) {
            ...
        },
        onError: function( filename, type, status, response ) {
            ...
        }
});

Which in turn sent the multipart request with header:

X-CSRF-TOKEN: abcdef01-2345-6789-abcd-ef0123456789

Their recommendations for embedding into <meta /> tags in the header would also work just fine by halting the request on submit, adding the header via javascript, and then finish submitting:

<html>
<head>
    <meta name="_csrf" content="${_csrf.token}"/>
    <!-- default header name is X-CSRF-TOKEN -->
    <meta name="_csrf_header" content="${_csrf.headerName}"/>
    <!-- ... -->
</head>
<body>
    <!-- ... -->
    <script>
        var token = $("meta[name='_csrf']").attr("content");
        var header = $("meta[name='_csrf_header']").attr("content");
        // Do whatever with values
    </script>
</body>
</html>

More info: Spring Security - CSRF for AJAX and JSON Requests

like image 28
rwyland Avatar answered Oct 04 '22 16:10

rwyland