OWASP CSRFGuard

Version: 1.1


Introduction

This document introduces OWASP CSRFGuard and further summarizes best practices and configuration recommendations for applications hosted on the WSO2 platform. In addition, this document further explains configuration values that can be fine-tuned to increase security, based on security requirements of the specific application.

OWASP CSRFGuard1 is an OWASP flagship project that provides synchronizer token pattern based CSRF protection in a comprehensive and customizable manner.

CSRFGuard offers complete protection over CSRF scenarios by covering HTTP POST, HTTP GET as well as AJAX based requests.

Forms based on HTTP POST and HTTP GET methods can be protected by injecting CSRF tokens into the “action” of the form, or by embedding a token in a hidden field. In addition, HTTP GET requests sent as a result of resource inclusions and links can also be protected by appending the relevant token in the “href” or “src” attributes. Token inclusions can be done manually using the provided JSP tag library or by using a JavaScript based automated injection mechanism. AJAX requests are protected by injecting an additional header which contains CSRF token.

Any state changing actions should be performed with the HTTP POST method, with an exception for the usage of PUT and DELETE methods in REST APIs.

CSRFGuard should not validate HTTP GET requests for CSRF protection. Token injection should be performed using JavaScript mechanism2. Hidden input field injection should be the only injection operation performed by the CSRFGuard which will protect HTTP POST based forms. In addition, AJAX POST requests should be protected by sending the CSRF token in a header.

CSRF token values should not be exposed in the URL. In situations where script injection can not be performed and built-in AJAX protection does not suffice, product teams may decide to use JST tag library based manual inclusion3, after verifying with the Security and Compliance Team.

Product teams should append CSRF exclusion URLs exposed from the root context, relevant to the particular product, in Owasp.CsrfGuard.Carbon.properties by following the steps mentioned in section 6.

Securing Web Applications

Recommended web.xml changes:

<!-- OWASP CSRFGuard context listener used to read CSRF configuration -->
<listener>
   <listener-class>org.owasp.csrfguard.CsrfGuardServletContextListener</listener-class>
</listener>

<!-- OWASP CSRFGuard session listener used to generate per-session CSRF token -->
<listener>
   <listener-class>org.owasp.csrfguard.CsrfGuardHttpSessionListener</listener-class>
</listener>

<!-- OWASP CSRFGuard per-application configuration property file location-->
<context-param>
   <param-name>Owasp.CsrfGuard.Config</param-name>
   <param-value>repository/conf/security/Owasp.CsrfGuard.Carbon.properties</param-value>
</context-param>

<!-- OWASP CSRFGuard filter used to validate CSRF token-->
<filter>
   <filter-name>CSRFGuard</filter-name>
   <filter-class>org.owasp.csrfguard.CsrfGuardFilter</filter-class>
</filter>

<!-- OWASP CSRFGuard filter mapping used to validate CSRF token-->
<filter-mapping>
   <filter-name>CSRFGuard</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

<!-- OWASP CSRFGuard servlet that serves dynamic token injection JavaScript (application can customize the URL pattern as required)-->
<servlet>
   <servlet-name>JavaScriptServlet</servlet-name>
   <servlet-class>org.owasp.csrfguard.servlet.JavaScriptServlet</servlet-class>
</servlet>
<servlet-mapping>
   <servlet-name>JavaScriptServlet</servlet-name>
   <url-pattern>/csrf.js</url-pattern>
</servlet-mapping>

Include JavaScriptServlet in the HTML template of the application, so that <head> element of all pages that need to be protected, should have JavaScriptServlet as the first JavaScript inclusion.

<html>
<head><script type=”text/javascript” src=”/csrf.js”></script>

<!-- other JavaScript inclusions should follow “csrf.js” inclusion -->
<script type=”text/javascript” src=”/main.js”></script></head>
<body>
...
</body>
</html>

Prepare and store per-application CSRF configuration files according to sections 5 and 6 of the document.

Securing Jaggery Applications

Update Jaggery version to 0.12.6

Recommended jaggery.conf changes:

"listeners" : [
    {
        "class" : "org.owasp.csrfguard.CsrfGuardServletContextListener" 
    },
    {
        "class" : "org.owasp.csrfguard.CsrfGuardHttpSessionListener"    
    }
],
"servlets" : [
    {
        "name" : "JavaScriptServlet",
        "class" : "org.owasp.csrfguard.servlet.JavaScriptServlet"
    }
],
"servletMappings" : [
    {
        "name" : "JavaScriptServlet",
        "url" : "/csrf.js"
    }
],
"contextParams" : [
    {
        "name" : "Owasp.CsrfGuard.Config",
        "value" : "repository/conf/security/Owasp.CsrfGuard.dashboard.properties"
    }
],
"filters" : [
    {
        "name" : "CSRFGuard",
        "class" : "org.owasp.csrfguard.CsrfGuardFilter"
    }
],
"filterMappings" : [
    {
        "name" : "CSRFGuard",
        "url" : "/*"
    }
],

Include JavaScriptServlet in the HTML template of the application, so that the <head> element of all pages that need to submit or make ajax requests to a protected URL should reference it as the first JavaScript inclusion.

<html>
<head><script type=”text/javascript” src=”/csrf.js”></script>

<!-- other JavaScript inclusions should follow “csrf.js” inclusion -->
<script type=”text/javascript” src=”/main.js”></script></head>
<body>
...
</body>
</html>

Prepare and store per-application CSRF configuration files according to sections 5 and 6 of the document.

Diff available at 4 highlights recommended changes, which are further listed below.

# WSO2 : Since state-changing operations are not performed via HTTP GET,
# disabling CSRF validation for GET method.
org.owasp.csrfguard.UnprotectedMethods=GET

# WSO2 : Considering overhead, necessity, as well as current unintended behaviour
# of library after blocking a CSRF attack, disabling per-page tokens.
org.owasp.csrfguard.TokenPerPage=false

# WSO2 : Disabling token rotation after blocking a CSRF attack, since this behaviour
# will break back navigation after blocking an attack.
#org.owasp.csrfguard.action.Rotate=org.owasp.csrfguard.action.Rotate

# WSO2 : Disable redirecting user to an error page after blocking a CSRF attack
#org.owasp.csrfguard.action.Redirect=org.owasp.csrfguard.action.Redirect
#org.owasp.csrfguard.action.Redirect.Page=%servletContext%/error.html

# WSO2 : Enable sending a 403 error after blocking a CSRF attack. Product teams 
# can add error page that handles 403 or “org.owasp.csrfguard.action.Error” to 
# display custom error pages. 
org.owasp.csrfguard.action.Error=org.owasp.csrfguard.action.Error
org.owasp.csrfguard.action.Error.Code=403
org.owasp.csrfguard.action.Error.Message=Security violation.

# WSO2 : Since, CSRFGuard will send relevant token name as HTTP header
# “X-” prefix was added to express that this is a non-standard header.
org.owasp.csrfguard.TokenName=X-CSRF-Token

# WSO2 : Disable printing configuration during start-up
org.owasp.csrfguard.Config.Print = false

# WSO2 : Disable JavaScript from injecting token value to HTTP GET based forms.
# This prevents token leakage that could occur when sending token in URL.
# State-changing actions should not be performed over HTTP GET
org.owasp.csrfguard.JavascriptServlet.injectGetForms = false

# WSO2 : Disable JavaScript from injecting token value to form action.
# This prevents token leakage that could occur when sending token in URL.
org.owasp.csrfguard.JavascriptServlet.injectFormAttributes = false

# WSO2 : Disable JavaScript from injecting token value to “src” and “href”.
# This prevents token leakage that could occur when sending token in URL.
org.owasp.csrfguard.JavascriptServlet.injectIntoAttributes = false 

# WSO2 : Changing X-Request-With header text to avoid unnecessary information disclosure.
org.owasp.csrfguard.JavascriptServlet.xRequestedWith = WSO2 CSRF Protection

# WSO2 - Pseudo-random number generator provider should be configured based on 
# environment (SUN/IBMJCE)
org.owasp.csrfguard.PRNG.Provider=SUN

Excluding URLs from CSRF Validation

Web applications can include property keys with org.owasp.csrfguard.unprotected. prefix to exclude relevant patterns from CSRF protection.

Example

org.owasp.csrfguard.unprotected.Default=%servletContext%/exampleAction
org.owasp.csrfguard.unprotected.Default_1=%servletContext%/exampleAction
org.owasp.csrfguard.unprotected.Example=%servletContext%/exampleAction/*
org.owasp.csrfguard.unprotected.ExampleRegEx=^%servletContext%/.*Public\.do$

Note

Do not use additional “.” (period) symbols in unprotected URL alias which is followed by org.owasp.csrfguard.unprotected.

Example relevant to above note:

Example Incorrect Usage

org.owasp.csrfguard.unprotected.auth.example=%servletContext%/auth

Example Correct Usage

org.owasp.csrfguard.unprotected.authExample=%servletContext%/auth

Further Enhancing Security

This section lists down configuration values that can be used to further enhance security or introduce additional restrictions. Changes to following configuration values should only be done based on customer request or justifiable application level requirements since they will affect performance or user experience.

The property below can be used to change the hashing algorithm used to generate CSRF token: org.owasp.csrfguard.PRNG=SHA1PRNG

Following property can be used to define the length of CSRF token:

org.owasp.csrfguard.TokenLength=32

Following property can be enabled to invalidate the user session if an CSRF attack attempt was blocked by CSRFGuard:

#org.owasp.csrfguard.action.Invalidate=org.owasp.csrfguard.action.Invalidate

WSO2 Product Integration Checklist

Follow this checklist when integrating WSO2 products with CSRFGuard.

Checklist Item 1

Make sure state changing actions are performed only with HTTP POST method, with an exception for the usage of PUT and DELETE methods in REST APIs. No state changing operation should happen through GET requests.

Checklist Item 2

CSRFGuard configuration should be changed to allow unauthenticated sessions to pass through the filter, by setting org.owasp.csrfguard.ValidateWhenNoSessionExists property to false in /repository/conf/security/Owasp.CsrfGuard.Carbon.properties. This can be done using product distribution POM file by adding below rule in tasks section of maven-antrun-plugin:

<!-- Update Owasp.CsrfGuard.properties file with ValidateWhenNoSessionExists to disable validation on requests made with no valid session -->
<replace  
    file="target/wso2carbon-core-${carbon.kernel.version}/repository/conf/security/
        Owasp.CsrfGuard.Carbon.properties" 
    token="org.owasp.csrfguard.ValidateWhenNoSessionExists = true" 
    value="org.owasp.csrfguard.ValidateWhenNoSessionExists = false"/>

Checklist Item 3 (For WSO2 Carbon 4.4.6 / 4.4.7 / 4.4.8 based products only)

CSRFGuard configuration location should be changed in web.xml to adapt to IBM JDK and DevStudio deployment environments. The default configuration location should be changed from /repository/conf/security/Owasp.CsrfGuard.Carbon.properties to repository/conf/security/Owasp.CsrfGuard.Carbon.properties. This can be done using the product distribution POM file by adding the below rule in the tasks section of maven-antrun-plugin (if any product completely replaces the web.xml file derived from carbon, the product should add this change in their version of web.xml) :

Note

This path correction should also be done in any web.xml files other than carbon web.xml, and also in jaggery.conf files, if a product contains such.

<!-- Update Owasp.CsrfGuard.properties file location to fix IBM JDK and DevStudio issue-->
<replace 
   file="target/wso2carbon-core-${carbon.kernel.version}/repository/conf/tomcat/carbon/
      WEB-INF/web.xml" 
   token="/repository/conf/security/Owasp.CsrfGuard.Carbon.properties" 
   value="repository/conf/security/Owasp.CsrfGuard.Carbon.properties"/>

Checklist Item 4

CSRFGuard configuration for the Carbon console is available at /repository/conf/security/Owasp.CsrfGuard.Carbon.properties​​. The product team should append product-specific CSRF exclusions for "Carbon console" to configuration file, during "distribution" maven build.

Product specific patterns used in the previous implementation are available at 5 for reference.

Refer to Excluding URLs from CSRF Validation section for more details on adding exclusion patterns.

Checklist Item 5

If a product contains Java web applications other than "Carbon console", make sure web.xml and template follow instructions in "Securing Web Applications" section.

While preparing the CSRFGuard configuration file for the web application, you may duplicate /repository/conf/security/Owasp.CsrfGuard.Carbon.properties​​ and make application specific changes (if there are any). Thereafter, use Owasp.CsrfGuard.Config context parameter to point to the configuration file location.

Checklist Item 6

If the product contains Jaggery web applications, make sure jaggery.conf and template follow the instructions in Securing Jaggery Applications section.

While preparing the CSRFGuard configuration file for the Jaggery application, you may duplicate /repository/conf/security/Owasp.CsrfGuard.Carbon.properties​​ and make application specific changes (if there are any). Thereafter, use Owasp.CsrfGuard.Config context parameter to point to the configuration file location.

Checklist Item 7

If any component made available within carbon context (root context) product contains a screen that is not rendered within the "Carbon console" template, but submits data to a “CSRF protected” resources exposed from "Carbon console" (root context) :

  • Components should include the CSRFGuard JavaScript as the first JavaScript inclusion in <head> section.
  • Example : TryIt (Example PR6)

Checklist Item 8

If any component contains JavaScript logic that generates forms dynamically using document.createElement('form') and submits the same to a CSRF-protected URL, it is required to add a CSRF token manually as a form element using taglib:

  • Add taglib to the jsp,
    <%@ taglib uri="http://www.owasp.org/index.php/Category:OWASP_CSRFGuard_Project/Owasp.CsrfGuard.tld" prefix="csrf" %>
    
  • Manually inject the csrf token to form
    var input = document.createElement("input"); 
    input.setAttribute('type',"hidden");
    input.setAttribute('name',"<csrf:tokenname/>");
    input.setAttribute('name',"<csrf:tokenvalue/>");
    form.appendChild(input);
    

Checklist Item 9

If any AJAX POST request sent to a CSRF-protected, relative path (path not starting with http:// or https://) contains a colon (:) in the request URL, CSRFGuard will fail to add an X-CSRF-Token header. This will result in a CSRF error. As a fix, it is required to include the CSRF token manually as a header similar to the following:

jQuery.ajax({
    type: "POST",
    url: "../eventreceiver/get_adapter_properties.jsp?name=example:1.0.0",
    data: {},
    contentType: "application/json; charset=utf-8",
    dataType: "text",
    beforeSend: function(xhr) {
        xhr.setRequestHeader("<csrf:tokenname/>","<csrf:tokenvalue/>");
    },
        success: function (propertiesString) {
        ...
    }
});

Checklist Item 10

If an integration test submits data to a URL protected by CSRFGuard, the test case should do the following to prevent test failures:

  • Call configured JavaScriptServlet with HTTP header FETCH-CSRF-TOKEN: 1 set to retrieve CSRF token for the current session.

    Example

    Request:

    curl 'https://localhost:9443/carbon/admin/js/csrfPrevention.js' -X POST -H 'FETCH-CSRF-TOKEN: 1' -H 'JSESSIONID=0C3C606B62FC7AC2D175AA1B9DCA971D;' --compressed --insecure
    

    Response: X-CSRF-Token:TCPT-DI3J-DVGZ-2684-NQ67-L9OR-DTZU-FW85

  • Send CSRF token received in response as a parameter of submission.

Checklist Item 11

For file uploads (multipart requests), the recommendation is to manually inject the CSRF token into the action of the form (including requests made to /fileupload path). To do so, the following steps need to be followed:

  • Add taglib to the jsp,

    <%@ taglib uri="http://www.owasp.org/index.php/Category:OWASP_CSRFGuard_Project/Owasp.CsrfGuard.tld" prefix="csrf" %>
    

  • Manually inject the CSRF token to form action

    action="../../fileupload/webapp?<csrf:tokenname/>=<csrf:tokenvalue/>"
    

References