6.7. CSRF Countermeasures

6.7.1. Overview

Cross-Site Request Forgery (hereinafter CSRF) is an attack that forces a user to perform unwanted actions on a different website in which the user is authenticated.
This is usually carried out by executing a script or by automatic transfer (HTTP redirect).
Following are the methods to prevent server side CSRF attack.
  • Embedding confidential information (token)
  • Re-entering password
  • ‘Refer’ check
In OWASP, the method to use token pattern is recommended.
csrf check other site

Picture - csrf check other site

Note

About OWASP

Open Web Application Security Project (OWASP) is not-for-profit international organization dedicated to enable organizations to develop and maintain applications that can be trusted. It advocates measures such as effective approach etc. with respect to security.

As mentioned earlier, there are multiple ways to avoid CSRF. However, Spring Security provides a library that uses fixed tokens.
In this, one fixed token is used for each session and the same value is used for all requests.
When the default HTTP method is other than GET, HEAD, TRACE and OPTIONS,
CSRF token included in the request is checked and if the values do not match, an error (HTTP Status: 403 [Forbidden]) is thrown.
csrf check other kind

Picture - csrf check other kind

Tip

CSRF token check is the check that verifies an invalid update request from a different site and throws an error. To check and enforce users to maintain the order (series of business flows), refer Transaction Token Check.

Warning

CSRF measures at the time of the multi-part request (file upload)

About CSRF measures during file upload, file upload Servlet Filter settingshould be followed.


6.7.2. How to use

6.7.2.1. Spring Security settings

Settings to use the CSRF functionality provided by Spring Security, are explained here. Use of web.xml configured using Spring Security’s How to use, is assumed.

6.7.2.1.1. spring-security.xml settings

The locations where additional settings are required, are highlighted.

 <sec:http auto-config="true" use-expressions="true" >
     <!-- omitted -->
     <sec:csrf />  <!-- (1) -->
     <sec:access-denied-handler ref="accessDeniedHandler"/>  <!-- (2) -->
     <!-- omitted -->
 </sec:http>

 <bean id="accessDeniedHandler"
     class="org.springframework.security.web.access.DelegatingAccessDeniedHandler">  <!-- (3) -->
     <constructor-arg index="0">  <!-- (4) -->
         <map>
             <entry
                 key="org.springframework.security.web.csrf.InvalidCsrfTokenException">  <!-- (5) -->
                 <bean
                     class="org.springframework.security.web.access.AccessDeniedHandlerImpl">  <!-- (5) -->
                     <property name="errorPage"
                         value="/WEB-INF/views/common/error/invalidCsrfTokenError.jsp" />  <!-- (5) -->
                 </bean>
             </entry>
             <entry
                 key="org.springframework.security.web.csrf.MissingCsrfTokenException">  <!-- (6) -->
                 <bean
                     class="org.springframework.security.web.access.AccessDeniedHandlerImpl">  <!-- (6) -->
                     <property name="errorPage"
                         value="/WEB-INF/views/common/error/missingCsrfTokenError.jsp" />  <!-- (6) -->
                 </bean>
             </entry>
         </map>
     </constructor-arg>
     <constructor-arg index="1">  <!-- (7) -->
         <bean
             class="org.springframework.security.web.access.AccessDeniedHandlerImpl">  <!-- (8) -->
             <property name="errorPage"
                 value="/WEB-INF/views/common/error/accessDeniedError.jsp" />  <!-- (8) -->
         </bean>
     </constructor-arg>
 </bean>
Sr. No. Description
(1)
Spring Security’s CSRF token check functionality can be used by defining <sec:csrf> element in <sec:http> element.
For HTTP methods that are checked by default, refer to Here.
For details, refer to Spring Security Reference Document.
(2)
Define Handler that changes the view to be displayed according to each type of exception when the exception inheriting AccessDeniedException occurs.
It is possible to display all the exceptions on the same screen by specifying the destination jsp in error-page attribute.
Refer to Here when the exceptions are not handled by Spring Security functionality.
(3)
To change error page, specify org.springframework.security.web.access.DelegatingAccessDeniedHandler in class of the Handler provided by Spring Security.
(4)
Using the first argument of constructor, set the screen that changes the display with each type of exception other than the default exceptions (exception that inherits AccessDeniedException) in Map format.
(5)
Specify the exception that inherits AccessDeniedException, in key.
Specify org.springframework.security.web.access.AccessDeniedHandlerImpl provided by Spring Security as the implementation class.
Specify the view to be displayed in value, by specifying errorPage in property name.
(6)
Define the change in display if the exception type differs from (5).
(7)
Specify the default view (exception that inherits AccessDeniedException and which is not specified by the first argument of constructor and AccessDeniedException) using second argument of constructor.
(8)
Specify org.springframework.security.web.access.AccessDeniedHandlerImpl provided by Spring Security, as the implementation class.
Specify the view to be displayed in value, by specifying errorPage in property name.

Type of exception occurred due to CSRF countermeasure, with inherited AccessDeniedException.
Exception Reason
org.springframework.security.web.csrf.
InvalidCsrfTokenException
It occurs when the CSRF token requested from client does not match with the CSRF token stored at server side.
org.springframework.security.web.csrf.
MissingCsrfTokenException
It occurs when the CSRF token does not exist at server side.
As per default setting, since the token is stored in HTTP session, having no CSRF token implies that the HTTP session is discarded (session timed out).

MissingCsrfTokenException occurs when storage location of CSRF token is changed to cache server or DB using token-repository-ref attribute of <sec:csrf> element, and when CSRF token is deleted from the storage location.
It implies that when token is not stored in HTTP session, session timeout cannot be detected using this functionality.

Note

When HTTP session is to be used as storage location of CSRF token, the session timeout can be detected for the request of CSRF token check.

The operation after detecting session timeout differs depending on the specifications of invalid-session-url attribute of <session-management> element.

  • When invalid-session-url attribute is specified, it is redirected to the path specified in invalid-session-url once the session is created.
  • When invalid-session-url attribute is not specified, it is handled according to the definition of org.springframework.security.web.access.AccessDeniedHandler specified in <access-denied-handler> element.

When session timeout needs to be detected for the request which does not fall under CSRF token check, it is advisable to detect it by specifying invalid-session-url attribute of <session-management> element. For details, refer to “Detecting session time-out”.


Note

Error handling when <sec:access-denied-handler> settings are omitted

It is possible to transit to any page by performing the following settings in web.xml.

web.xml

<error-page>
    <error-code>403</error-code>  <!-- (1) -->
    <location>/WEB-INF/views/common/error/accessDeniedError.jsp</location>  <!-- (2) -->
</error-page>
Sr. No. Description
(1)
Set status code 403 in error-code element.
(2)
Set transition path in location element.

Note

When status code other than 403 is to be returned

If status code other than 403 is to be returned when the CSRF token in request does not match, it is necessary to create an independent AccessDeniedHandler that implements org.springframework.security.web.access.AccessDeniedHandler interface.

6.7.2.1.2. spring-mvc.xml settings

By using the RequestDataValueProcessor implementation class for CSRF token, the token can be automatically inserted as ‘hidden’ field using <form:form> tag of Spring tag library.

 <bean id="requestDataValueProcessor"
     class="org.terasoluna.gfw.web.mvc.support.CompositeRequestDataValueProcessor"> <!-- (1)  -->
     <constructor-arg>
         <util:list>
             <bean
                 class="org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor" /> <!-- (2)  -->
             <bean
                 class="org.terasoluna.gfw.web.token.transaction.TransactionTokenRequestDataValueProcessor" />
         </util:list>
     </constructor-arg>
 </bean>
Sr. No. Description
(1)
Perform bean definition for org.terasoluna.gfw.web.mvc.support.CompositeRequestDataValueProcessor for which multiple
org.terasoluna.gfw.web.mvc.support.RequestDataValueProcessorcan be defined.
(2)
Set the bean definition org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor as the first argument of constructor.

Note

CSRF token is created and checked by ``CsrfFilter`` which is enabled by setting <sec:csrf />. Therefore, the developer need not be particularly aware of the CSRF measures in Controller.

6.7.2.2. Sending CSRF token using form

To send CSRF token using form in JSP, any one of the following processes need to be performed.

  • Automatically adding <input type="hidden"> tag with inserted CSRF token, using <form:form> tag.
  • Explicitly adding <input type="hidden"> tag with inserted CSRF token, using <sec:csrfInput/> tag.

6.7.2.2.1. How to insert CSRF token automatically

When CsrfRequestDataValueProcessor is defined as per spring-mvc.xml settings, <input type="hidden"> tag with CSRF token is automatically added using <form:form> tag.

CSRF token need not be considered in JSP implementation.

<form:form method="POST"
  action="${pageContext.request.contextPath}/csrfTokenCheckExample">
  <input type="submit" name="second" value="second" />
</form:form>

Following HTML is output.

<form action="/terasoluna/csrfTokenCheckExample" method="POST">
  <input type="submit" name="second" value="second" />
  <input type="hidden" name="_csrf" value="dea86ae8-58ea-4310-bde1-59805352dec7" /> <!-- (1) -->
</form>
Sr. No. Description
(1)
In Spring Security default implementation, CSRF token is inserted by adding <input type="hidden"> tag where _csrf is set in name attribute.

CSRF token is created at login.

Tip

If CsrfRequestDataValueProcessor is used in Spring 4, CSRF token inserted <input type="hidden"> tag is output, only if the value specified in method attribute of <form:form> tag matches with HTTP methods (HTTP methods other than GET, HEAD, TRACE, OPTIONS in Spring Security default implementation) of CSRF token check.

For example, when GET method is specified in method attribute as shown below, CSRF token inserted <input type="hidden"> tag is not output.

<form:form method="GET" modelAttribute="xxxForm" action="...">
    <%-- ... --%>
</form:form>

This is as per the following description

The unique token can also be included in the URL itself, or a URL parameter. However, such placement runs a greater risk that the URL will be exposed to an attacker, thus compromising the secret token.

in OWASP Top 10 and it helps in building a secure Web application.

6.7.2.2.2. How to explicitly insert CSRF token

When <form:form> tag is not to be used, <sec:csrfInput/> tag needs to be added explicitly.

When using <sec:csrfInput/> tag, CSRF token inserted <input type="hidden"> tag is output.

<form method="POST"
  action="${pageContext.request.contextPath}/csrfTokenCheckExample">
    <input type="submit" name="second" value="second" />
    <sec:csrfInput/>  <!-- (1) -->
</form>

Following HTML is output.

<form action="/terasoluna/csrfTokenCheckExample" method="POST">
  <input type="submit" name="second" value="second" />
  <input type="hidden" name="_csrf" value="dea86ae8-58ea-4310-bde1-59805352dec7"/>  <!-- (2) -->
</form>
Sr. No. Description
(1)
Specify <sec:csrfInput/> tag to output CSRF token inserted <input type="hidden"> tag.
(2)
In spring Security default implementation, CSRF token is inserted by adding <input type="hidden"> tag where _csrf is set in name attribute.

Note

When there is no CSRF token in the CSRF token check request (when HTTP default method is other than GET, HEAD, TRACE and OPTIONS) or when the token value stored on server is different than the token value sent, access denial process is performed using AccessDeniedHandler and HttpStatus 403 is returned. The specified error page is displayed when spring-security.xml settings are described.

6.7.2.3. Sending CSRF token using Ajax

CsrfFilter which is enabled by setting <sec:csrf />, fetches CSRF token not only from request parameter but also from
the HTTP request header.
When Ajax is used, it is recommended to set CSRF token in HTTP header. Even if request is sent in JSON format, it supports CSRF token check.

Note

When CSRF token is sent from both the HTTP header and request parameter, HTTP header value is given priority.

It is explained by using an example in Ajax. The locations for which additional setting is required are highlighted.

jsp implementation

 <!-- omitted -->
 <head>
   <sec:csrfMetaTags />  <!-- (1) -->

   <!-- omitted -->
 </head>
 <!-- omitted -->
 <script type="text/javascript">
 var contextPath = "${pageContext.request.contextPath}";
 var token = $("meta[name='_csrf']").attr("content");  <!-- (2) -->
 var header = $("meta[name='_csrf_header']").attr("content");  <!-- (3) -->
 $(document).ajaxSend(function(e, xhr, options) {
     xhr.setRequestHeader(header, token);  <!-- (4) -->
 });

 $(function() {
     $('#calcButton').on('click', function() {
         var $form = $('#calcForm'),
              $result = $('#result');
         $.ajax({
             url : contextPath + '/sample/calc',
             type : 'POST',
             data: $form.serialize(),
         }).done(function(data) {
             $result.html('add: ' + data.addResult + '<br>'
                          + 'subtract: ' + data.subtractResult + '<br>'
                          + 'multiply: ' + data.multipyResult + '<br>'
                          + 'divide: ' + data.divideResult + '<br>'); // (5)
         }).fail(function(data) {
             // error handling
             alert(data.statusText);
         });
     });
 });
 </script>
Sr. No. Description
(1)
By setting <sec:csrfMetaTags /> tag, the following meta tags are output by default.
  • <meta name="_csrf_parameter" content="_csrf" />
  • <meta name="_csrf_header" content="X-CSRF-TOKEN" />
  • <meta name="_csrf" content="dea86ae8-58ea-4310-bde1-59805352dec7" />(random UUID is set as a value of content attribute)
(2)
Fetch CSRF token set in <meta name="_csrf ...> tag.
(3)
Fetch CSRF header name set in <meta name="_csrf_header" ...> tag.
(4)
Set the header name (default:X-CSRF-TOKEN) fetched from <meta> tag and CSRF token value, in request header.
(5)
As this code may cause XSS attack, care should be taken while actually writing the JavaScript code.
In this example, there is no issue as all the fields namely data.addResult, data.subtractResult, data.multipyResult and data.divideResult are numerical.

When sending a request in JSON format, it is advisable to set HTTP header in the same way.

Todo

It needs to be modified since Ajax example is missing.

6.7.2.4. Considerations at the time of multipart request (file upload)

Generally, when a multipart request such as file upload is sent, the value sent by form cannot be fetched in Filter.
Therefore, just by the explanation so far, CsrfFilter is unable to fetch CSRF token at the time of multipart request and thus it is considered as an invalid request.

Hence, countermeasures need to be implemented using any one of the following methods.

  • Using org.springframework.web.multipart.support.MultipartFilter.
  • Sending CSRF token using query parameter

Note

Since there are merits and demerits respectively, determine the countermeasure method to be used considering system requirements.

For file upload details, refer to FileUpload.

6.7.2.4.1. How to use MultipartFilter

In case of multipart request, normally the value sent from form cannot be fetched in Filter.
By using org.springframework.web.multipart.support.MultipartFilter, the value sent by form
can be fetched in Filter even for a multipart request.

Warning

When MultipartFilter is used, the upload process is carried out before performing authentication/authorization using springSecurityFilterChain, thereby allowing unauthenticated/unauthorized user to carry out the uploading (temporary file creation).

To use MultipartFilter, following settings are recommended.

web.xml settings

<filter>
    <filter-name>MultipartFilter</filter-name>
    <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class> <!-- (1) -->
</filter>
<filter>
    <filter-name>springSecurityFilterChain</filter-name> <!-- (2) -->
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>MultipartFilter</filter-name>
    <servlet-name>/*</servlet-name>
</filter-mapping>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
Sr. No. Description
(1)
Define org.springframework.web.multipart.support.MultipartFilter.
(2)
MultipartFilter should be defined before springSecurityFilterChain.

JSP implementation

<form:form action="${pageContext.request.contextPath}/fileupload"
    method="post" modelAttribute="fileUploadForm" enctype="multipart/form-data">  <!-- (1) -->
    <table>
        <tr>
            <td width="65%"><form:input type="file" path="uploadFile" /></td>
        </tr>
        <tr>
            <td><input type="submit" value="Upload" /></td>
        </tr>
    </table>
</form:form>
Sr. No. Description
(1)
When CsrfRequestDataValueProcessor is defined as per spring-mvc.xml settings,
by using <form:form> tag, <input type="hidden"> tag with CSRF token is added automatically.
Therefore, CSRF token need not be considered in JSP implementation.

When using <form> tag
CSRF token should be set by How to explicitly insert CSRF token.

6.7.2.4.2. How to send CSRF token using query parameter

To avoid uploading (temporary file creation) by unauthorized/unauthenticated user, CSRF token should be sent using query parameter instead of using MultipartFilter.

Warning

When CSRF token is sent using this method,

  • CSRF token is displayed in browser address bar
  • When bookmarked, CSRF token is registered in bookmark
  • CSRF token is registered in access log of Web server

Therefore, risk of misusing CSRF token by attacker is higher as compared to the method using MultipartFilter.

As per default implementation of Spring Security, random UUID is generated as CSRF token value, therefore, session would not be hijacked even though CSRF token is leaked.

Example of implementation where CSRF token is sent as query parameter is given below.

JSP implementation

<form:form action="${pageContext.request.contextPath}/fileupload?${f:h(_csrf.parameterName)}=${f:h(_csrf.token)}"
    method="post" modelAttribute="fileUploadForm" enctype="multipart/form-data"> <!-- (1) -->
    <table>
        <tr>
            <td width="65%"><form:input type="file" path="uploadFile" /></td>
        </tr>
        <tr>
            <td><input type="submit" value="Upload" /></td>
        </tr>
    </table>
</form:form>
Sr. No. Description
(1)
Following query needs to be assigned to the action attribute of <form:form> tag.
?${f:h(_csrf.parameterName)}=${f:h(_csrf.token)}
Same setting is required even when using <form> tag.