6.7. CSRF Countermeasures¶
Caution
This version is already obsolete. Please check the latest guideline.
Table of contents
6.7.1. Overview¶
- Embedding confidential information (token)
- Re-entering password
- ‘Refer’ check
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.
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.
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.
|
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 ininvalid-session-url
once the session is created. - When
invalid-session-url
attribute is not specified, it is handled according to the definition oforg.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 multipleorg.terasoluna.gfw.web.mvc.support.RequestDataValueProcessor can 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 fromNote
When CSRF token is sent from both the HTTP header and request parameter, HTTP header value is given priority.
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.
|
(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)¶
Filter
.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¶
Filter
.org.springframework.web.multipart.support.MultipartFilter
, the value sent by formFilter
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. |