title: Spring CSRF token does not work, when the request to be sent is a multipart request
body: <p>I use,</p>
  
  <ul>
  <li>Spring Framework 4.0.0 RELEASE (GA)</li>
  <li>Spring Security 3.2.0 RELEASE (GA)</li>
  <li>Struts 2.3.16</li>
  </ul>
  
  <p>In which, I use an in-built security token to guard against CSRF attacks.</p>
  
  <p>The Struts form looks like the following.</p>
  
  <pre><code>&lt;s:form namespace="/admin_side"
          action="Category"
          enctype="multipart/form-data"
          method="POST"
          validate="true"
          id="dataForm"
          name="dataForm"&gt;
  
      &lt;s:hidden name="%{#attr._csrf.parameterName}"
                value="%{#attr._csrf.token}"/&gt;
  &lt;/s:form&gt;
  </code></pre>
  
  <p>The generated HTML code is as follows.</p>
  
  <pre><code>&lt;form id="dataForm"
        name="dataForm"
        action="/TestStruts/admin_side/Category.action"
        method="POST"
        enctype="multipart/form-data"&gt;
  
      &lt;input type="hidden"
             name="_csrf"
             value="3748c228-85c6-4c3f-accf-b17d1efba1c5" 
             id="dataForm__csrf"&gt;
  &lt;/form&gt;
  </code></pre>
  
  <p><strong>This works fine, unless the request is multipart</strong> in which case, the request ends with the status code 403.</p>
  
  <blockquote>
    <p><strong>HTTP Status 403</strong> - Invalid CSRF Token 'null' was found on the request
    parameter '_csrf' or header 'X-CSRF-TOKEN'.</p>
    
    <p><strong>type</strong> Status report</p>
    
    <p><strong>message</strong> Invalid CSRF Token 'null' was found on the request parameter
    '_csrf' or header 'X-CSRF-TOKEN'.</p>
    
    <p><strong>description</strong> Access to the specified resource has been forbidden.</p>
  </blockquote>
  
  <p>The <code>spring-security.xml</code> file is as follows.</p>
  
  <pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
  &lt;beans:beans xmlns="http://www.springframework.org/schema/security"
               xmlns:beans="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-4.0.xsd
             http://www.springframework.org/schema/security
             http://www.springframework.org/schema/security/spring-security-3.2.xsd"&gt;
  
      &lt;http pattern="/Login.jsp*" security="none"&gt;&lt;/http&gt;
  
      &lt;http auto-config='true' use-expressions="true" disable-url-rewriting="true" authentication-manager-ref="authenticationManager"&gt;
          &lt;session-management session-fixation-protection="newSession"&gt;
              &lt;concurrency-control max-sessions="1" error-if-maximum-exceeded="true" /&gt;
          &lt;/session-management&gt;
  
          &lt;csrf/&gt;
  
          &lt;headers&gt;
              &lt;xss-protection /&gt;
              &lt;frame-options /&gt;
              &lt;!--&lt;cache-control /&gt;--&gt;
              &lt;!--&lt;hsts /&gt;--&gt;
              &lt;content-type-options /&gt; &lt;!--content sniffing--&gt;
          &lt;/headers&gt;
  
          &lt;intercept-url pattern="/admin_side/**" access="hasRole('ROLE_ADMIN')" requires-channel="any"/&gt;
          &lt;form-login login-page="/admin_login/Login.action" authentication-success-handler-ref="loginSuccessHandler" authentication-failure-handler-ref="authenticationFailureHandler"/&gt;
          &lt;logout logout-success-url="/admin_login/Login.action" invalidate-session="true" delete-cookies="JSESSIONID"/&gt;
      &lt;/http&gt;
  
      &lt;beans:bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/&gt;
  
      &lt;beans:bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider"&gt;
          &lt;beans:property name="userDetailsService" ref="userDetailsService"/&gt;
          &lt;beans:property name="passwordEncoder" ref="encoder" /&gt;
      &lt;/beans:bean&gt;
  
      &lt;beans:bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager"&gt;
          &lt;beans:property name="providers"&gt;
              &lt;beans:list&gt;
                  &lt;beans:ref bean="daoAuthenticationProvider" /&gt;
              &lt;/beans:list&gt;
          &lt;/beans:property&gt;
      &lt;/beans:bean&gt;
  
      &lt;authentication-manager&gt;
          &lt;authentication-provider user-service-ref="userDetailsService"&gt;
          &lt;/authentication-provider&gt;
      &lt;/authentication-manager&gt;
  
      &lt;beans:bean id="loginSuccessHandler" class="loginsuccesshandler.LoginSuccessHandler"/&gt;
      &lt;beans:bean id="authenticationFailureHandler" class="loginsuccesshandler.AuthenticationFailureHandler" /&gt;
  
      &lt;global-method-security secured-annotations="enabled" proxy-target-class="false" authentication-manager-ref="authenticationManager"&gt;
          &lt;protect-pointcut expression="execution(* admin.dao.*.*(..))" access="ROLE_ADMIN"/&gt;
      &lt;/global-method-security&gt;
  &lt;/beans:beans&gt;
  </code></pre>
  
  <p>So, where to look for this token, when a request is multipart? (This should not be related to Struts at all.)</p>
  
  <p><sub>The implementation of <code>UserDetailsService</code> can be found in <a href="https://stackoverflow.com/q/20919913/1391249">this</a> earlier question of mine, if needed.</sub></p>
  
  <hr>
  
  <p><a href="http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#csrf-multipartfilter" rel="nofollow noreferrer">Placing <code>MultipartFilter</code> before Spring Security</a> did not help either.</p>
  
  <p>The <code>web.xml</code> file looks like the following.</p>
  
  <pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
  &lt;web-app version="3.0"
           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-app_3_0.xsd"&gt;
  
      &lt;context-param&gt;
          &lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;
          &lt;param-value&gt;
              /WEB-INF/applicationContext.xml
              /WEB-INF/spring-security.xml
          &lt;/param-value&gt;
      &lt;/context-param&gt;
  
      &lt;filter&gt;
          &lt;filter-name&gt;MultipartFilter&lt;/filter-name&gt;
          &lt;filter-class&gt;org.springframework.web.multipart.support.MultipartFilter&lt;/filter-class&gt;
      &lt;/filter&gt;
  
      &lt;filter&gt;
          &lt;filter-name&gt;springSecurityFilterChain&lt;/filter-name&gt;
          &lt;filter-class&gt;org.springframework.web.filter.DelegatingFilterProxy&lt;/filter-class&gt;
      &lt;/filter&gt;
  
      &lt;filter-mapping&gt;
          &lt;filter-name&gt;MultipartFilter&lt;/filter-name&gt;
          &lt;servlet-name&gt;/*&lt;/servlet-name&gt;
      &lt;/filter-mapping&gt;
  
      &lt;filter-mapping&gt;
          &lt;filter-name&gt;springSecurityFilterChain&lt;/filter-name&gt;
          &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
      &lt;/filter-mapping&gt;
  
      &lt;filter&gt;
          &lt;filter-name&gt;AdminLoginNocacheFilter&lt;/filter-name&gt;
          &lt;filter-class&gt;filter.AdminLoginNocacheFilter&lt;/filter-class&gt;
      &lt;/filter&gt;
  
      &lt;filter-mapping&gt;
          &lt;filter-name&gt;AdminLoginNocacheFilter&lt;/filter-name&gt;
          &lt;url-pattern&gt;/admin_login/*&lt;/url-pattern&gt;
      &lt;/filter-mapping&gt;
  
      &lt;filter&gt;
          &lt;filter-name&gt;NoCacheFilter&lt;/filter-name&gt;
          &lt;filter-class&gt;filter.NoCacheFilter&lt;/filter-class&gt;
      &lt;/filter&gt;
  
      &lt;filter-mapping&gt;
          &lt;filter-name&gt;NoCacheFilter&lt;/filter-name&gt;
          &lt;url-pattern&gt;/admin_side/*&lt;/url-pattern&gt;
      &lt;/filter-mapping&gt;
  
      &lt;listener&gt;
          &lt;listener-class&gt;org.springframework.web.context.ContextLoaderListener&lt;/listener-class&gt;
      &lt;/listener&gt;
  
      &lt;listener&gt;
          &lt;description&gt;Description&lt;/description&gt;
          &lt;listener-class&gt;org.springframework.web.context.request.RequestContextListener&lt;/listener-class&gt;
      &lt;/listener&gt;
  
      &lt;listener&gt;
          &lt;listener-class&gt;org.springframework.security.web.session.HttpSessionEventPublisher&lt;/listener-class&gt;
      &lt;/listener&gt;
  
      &lt;filter&gt;
          &lt;filter-name&gt;struts2&lt;/filter-name&gt;
          &lt;filter-class&gt;org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter&lt;/filter-class&gt;
          &lt;init-param&gt;
              &lt;param-name&gt;struts.devMode&lt;/param-name&gt;
              &lt;param-value&gt;true&lt;/param-value&gt;
          &lt;/init-param&gt;
      &lt;/filter&gt;
  
      &lt;filter-mapping&gt;
          &lt;filter-name&gt;struts2&lt;/filter-name&gt;
          &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
      &lt;/filter-mapping&gt;
  
      &lt;session-config&gt;
          &lt;session-timeout&gt;
              30
          &lt;/session-timeout&gt;
      &lt;/session-config&gt;
      &lt;welcome-file-list&gt;
          &lt;welcome-file&gt;index.jsp&lt;/welcome-file&gt;
      &lt;/welcome-file-list&gt;
  &lt;/web-app&gt;
  </code></pre>
  
  <p>It only works, when the token is appended as a query-string parameter as follows which is however, discouraged.</p>
  
  <pre><code>&lt;s:form namespace="/admin_side"
          action="Category?%{#attr._csrf.parameterName}=%{#attr._csrf.token}"
          enctype="multipart/form-data"
          method="POST"
          validate="true"
          id="dataForm"
          name="dataForm"&gt;
      ...
  &lt;s:form&gt;
  </code></pre>