Protection against Cross-Site Request Forgery (CSRF) attacks

Cross-site request forgery (CSRF/XSRF) attacks are powerful and straight-forward attacks that can totally subvert the security of Web applications. These attacks can target both the integrity (causing changes on the server without users approval) and the confidentiality (leaking private data) of Web applications.

How CSRF attacks work

Unlike Cross-Site Scripting (XSS) attacks, CSRF/XSRF attacks do not require an application bug that allows an attacker to inject active code into the Web-application. Instead, CSRF attacks work as soon the user opens a rogue Web application in parallel to the target application (for example, in a separate browser tab) or subsequent to a visit to the target Web application if that application does not clean up user credentials when the user navigates away. The rogue application can misuse the user's privileges and cause server-side actions on the user's behalf. To a lesser extent, it can also retrieve confidential data, for example, from JSON requests using a special form of CSRF called JavaScript™ hijacking attacks. For more information about hijacking attacks, see the Fortify site (http://www.fortifysoftware.com) and search for "JavaScript Hijacking".

Hence, these attacks are very powerful and Open Web Application Security Project(OWASP) lists them in the Top 10 Web application vulnerabilities. For more information see:

  • The OWASP site (http://www.owasp.org/)
  • Wikipedia for articles on Cross-site request forgery

While there a number of best practices out in the field, many:

  • Are wrong, for example using POST instead of GET does not protect against CSRF
  • Are very incomplete and limiting, for example not using arrays in JSON responses
  • Require quite a bit of effort by an application developer

To ease the development of secure application without burdening the application overtly, IBM® WebSphere® sMash provides corresponding countermeasures built into the framework.

CSRF Protection requires the specification of a secret token that is leveraged to encrypt the content of the csrf token when it is sent to the client. Please refer to the article "Secret key encryption" for more details.

Enabling CSRF protection

To enable CSRF protection, add a dependency to the zero.acf package. Note that this also enables active content filtering. If you have a simple application which uses Dojo exclusively to perform XHR requests, this is the only step you have to perform.

If you do not fall in this class or you need additional control over the protection-mechanism, e.g., to reduce the performance overhead, WebSphere sMash gives you finer control. Furthermore, in the transparent by-default mode, WebSphere sMash will not protect you against a more sophisticated class of CSRF attacks for which you have to enable strong CSRF protection explicitly.

Below sections give you details on how you can refine and strengthen your CSRF Protection.

However, before digging deeper into more advanced configuration, let us look first closer on how the CSRF protection mechanism works.

CSRF protection

To protect against CSRF attacks, WebSphere sMash requires that each request for a protected server-side resource or action provides a proof that it originates from an authenticated user's browser and the request is initiated from your own code. Here code is loosely defined as the union of HTML, JavaScript code and CSS. That is, CSRF protection requires a form of code-credentials. For that, Project WebSphere sMash will transparently create an additional CSRF credential during initial authentication, provide it to your own code and verify its presence for each request to a protected resource.

Code instrumentation

To provide above mentioned CSRF credential to each of your code pieces referencing protected server resources, the code has to be correspondingly instrumented. By default this happens by transparently rewriting the response from your server but WebSphere sMash also gives you explicit options, e.g., for the case when your applications falls outside the transparently covered application patterns.

There are two, not necessarily mutually exclusive, modes of operation of code instrumentation:

Transparent Instrumentation
For applications in which all access to protected resources is either through static links in HTML pages or through XHR in Dojo, CSRF-protection is completely transparent.
Programmatic Instrumentation
Applications that access protected resources through other means and cannot tolerate the performance overhead due to the code-rewriting of the transparent mode, or require maximal security can enable CSRF-protection by instrumenting their code programmatically.

Transparent Code Instrumentation

The most convenient way to instrument applications is to rely on the transparent rewriting of HTML files on the server-side and client-side instrumentation of AJAX code.

Server-side HTML

To transparently embed CSRF protection code into an HTML page requires the use of the ACF library in WebSphere sMash. Simply including a dependency of zero.acf will enable transparent code instrumentation for all HTML files.

To get finer control over where and how the transparent code instrumentation is performed, you can turn off automatic code instrumentation via following zero.config directive

/config/acf/enableByDefault=false
/config/security/token/enableCsrfProtection = "REQUEST"
/config/security/token/enableStrongCsrfProtection=true

(note that you must disable also ACF by-default to get fine-granular control, see below Notes section for more information on creating customized rules covering CSRF and ACF). After including the ACF library and resolving the ACF dependency for your application, add one or more of the following rule to your zero.config file

@include "${/config/dependencies/zero.acf}/config/acf.config"
This rule has the following mandatory parameters:
uri
A URI pattern of the files that have to be rewritten. The URI patterns are the same regular expression patterns that are supported as part of the condition operators in WebSphere sMash.
filterRuleFile
The rules governing which element referencing server-side resources are enhanced with CSRF protection. There are two default rules:
${/config/dependencies/zero.acf}/config/acf-config.csrf.xml
Rewrites all references to server-resources.
${/config/dependencies/zero.acf}/config/acf-config.csrf-safe.xml
Rewrites only references to server-resources that cannot cause leakage through HTTP referer fields.

For example, to rewrite the pages of /app use the following rule:

@include "acf.config" {
   "conditions": "/request/path =~ /app/(.*)?",
   "filterRuleFile" : "${/config/dependencies/zero.acf}/config/acf-config.csrf.xml"
}

For more information on leakage through HTTP referer fields, see the Best practices section.

  • Rewriting only rewrites the HTML part of the file. For embedded JavaScript code, see the Client-side JavaScript information for instrumenting client-side code. Similarly, this does not cover external CSS files. While they are integrity-sensitive (CSS can contain active code and hence they are best retrieved over SSL), they rarely are confidentiality-sensitive and hence would reference only public objects not requiring any rewriting. If not, they would have to be manually instrumented using the manual server-side instrumentation steps described in the Server side section.
  • HTML <BASE> elements are only supported if they point to the same server, otherwise they are removed. If they were not removed, this would cause a security exposure.
  • If you are also using Active content filtering on the same resources, you either have to use the merged config file ${/config/dependencies/zero.acf}/config/acf-config.default+csrf.xml or need to manually merge the CSRF-protection configuration ${/config/dependencies/zero.acf}/config/acf-config.csrf.xml with whatever part of the ACF rules (${/config/dependencies/zero.acf}/config/acf-config.default.xml) you want into a private configuration file. See the Configuring ACF section of the Active content filtering article for more information.
  • To limit the scope of rewriting to only parts of your file (for example, to better contain the exposure of CSRF credentials) and not the whole file, use a custom rule file. Using acf-csrf-config.xml or acf-csrf-safe-config.xml as reference points, create a new rule file in your config directory and add or remove items from the list of rewritten tags or attributes. You can also define a more restrictive <TARGET> element. To do this, see the Configuring ACF section of the Active content filtering article for more information.
  • Similar to ACF filtering, CSRF-rewriting can be applied to the body of authentication-induced redirects if the access to the rewritten resource causes authentication. However, this should be harmless.

Client-side JavaScript

For client-side JavaScript that uses the XHR methods provided by Dojo (versions 0.9 and 1.X), the HTML page should include the following scripts after loading dojo.js:
  • /zero/js/csrfBasic.js and
  • /zero/js/csrfDojo.js, .

All XHR methods are automatically instrumented for CSRF protection. If you enable server-side transparent protection mode, then the re-writer automatically and transparently detects when you load Dojo and include the two script files.

  • Do not use <BASE> elements on pages including CSRF-instrumented JavaScript code. If you have to use them you must ensure that the corresponding href attribute points to the local server. Otherwise, your security is compromised.
  • The preceding wrapper does not handle dynamically created HTML elements (for example <script>, <img>, <iframe> or <form>). If they point to protected resources, you must manually instrument the code as described in the following information.
  • If you are using an AJAX framework other than Dojo, you might achieve a similar transparent handling by applying the zeroClient.CSRFXhrWrapper.wrapXhr() function from the script /zero/js/csrfDojo.js similar to its use in zeroClient.CSRFXhrWrapper.wrapDojo().

Programmatic Code-Instrumentation

The methods described in the previous section should suffice for typical programs and are fail-safe to use. However, the transparent rewriting might not meet your performance goals or might not be sufficient if you create dynamic HTML elements pointing to protected resources or have CSS pointing to protected resources. In this case, use the programmatic methods described in this section.

The following methods are as fail-safe as possible. They determine automatically whether, for a given URI, a CSRF authenticator can be sent. That is, even if you accidentally pass a URL to a remote site (which at best could not make use of the authenticator and at worst might misuse it) the functions will not include the authenticator for such requests. The following best practices can maximize your security:

  • JavaScript embedding is preferred over server-side embedding as this minimizes the exposure of the code credential.
  • There are three different ways to encode CSRF authenticators:
    1. HTTP Header
    2. Parameter in form
    3. URI query string
    Use them in this order of preference. If possible, attach the authenticator as an HTTP header and use URI query string encoding only as last resort as this can cause leaking the secret (in referer fields or in log-files) in proxy servers.

Server-side

The programmatic API on the server-side consists of the following functions:

Java
The API is provided by the class zero.core.security.CSRFProtector in the following methods:
public static boolean doIncludeProtection(java.net.URI uri)
public static java.lang.String getProtectionFormField(java.lang.String path)
public static java.lang.String getProtectionFormField(java.net.URI uri)
public static java.lang.String getProtectedUri(java.lang.String path)
public static java.net.URI getProtectedUri(java.net.URI uri)

Please refer to Javadoc of class zero.core.security.CSRFProtector for more information.

Groovy
The functions have the same semantics as the String versions of Java methods getProtectionFormField and getProtectedUri, respectively:
getCSRFProtectedUri (String uri)
getCSRFProtectionFormField (String uri)
PHP
The following code sample shows the PHP version:
csrf_protected_form_field (String uri)
csrf_protected_uri (String uri)
For example, to protect a form using Groovy, you might issue the following code:
<% formUri =  getRelativeUri("/resources/owners"+ '/' + request.subject['remoteUser'] + "/todos")  %>
<form method="POST" action="<%= formUri %>" >
<%= getCSRFProtectionFormField(formUri) %>
... further fields
<input type="submit" name=".submit" value="Post Form" />
</form>

Client-side JavaScript

For client-side JavaScript that does not exclusively use the XHR methods provided by Dojo, the APIs in /zero/js/csrfBasic.js can be used. The following functions can be called on the object zeroClient.CSRFBasic:

addCSRFTokenToHeader(/*String*/ url, /*XHR*/ xhr)
addCSRFTokenToForm(/*Form*/ form)
addCSRFTokenToURL(/*String*/ url)
removeCSRFTokenFromForm(/*Form*/ form)

The following low-level methods are available if you want to lookup and attach the CSRF token manually:

getCSRFParamName(/*String*/ param)
getCSRFHeaderName(/*String*/ header)
getCSRFTokenValue(/*String*/ token)

To issue a protected XHR call, you might write JavaScript code as shown in the following example:

function getTodosXHR() {
        var url = ownerURL + '/' + owner + '/todos';
        var req = new XMLHttpRequest();
        req.open('POST', url, false);
        zeroClient.CSRFBasic.addCSRFTokenToHeader(url, req);
        req.send();
}

Configuration

Enabling

To enable CSRF protection, add /config/security/token/enableCsrfProtection="REQUEST" or /config/security/token/enableCsrfProtection="REQUEST|RESPONSE" to your zero.config file. The latter will enable transparent CSRF code-instrumentation for all HTML files (i.e., is the default when adding a dependency on zero.acf); the former enables just CSRF protection but requires you to manually configure the code instrumentation as outlined above. To disable CSRF protection, set the configuration parameter to the empty string ("").

Note that CSRF protection does not necessarily require the zero.acf dependency: as long as you use the programmatic APIs for code instrumentation zero.core is sufficient.

Credential Strength

To protected the CSRF credential consistently, they will be protected with the same strength as the authentication mechanism: That is, if authentication is over HTTPS, the CSRF credential will be sent only for requests over HTTPS and code instrumentation will be done only for responses over HTTPS; if the authentication request is over HTTP, the csrf credential will be sent over both HTTP and HTTPS. If the global configuration parameter /config/security/token/securedCookie is set to true, then CSRF credentials are sent only over HTTPS, regardless of authentication.

Strong CSRF Protection

From a user's perspective, authentication to a site does not immediately imply authorization for an operation on that site. An unauthenticated resource request, to a CSRF-protected resource, triggers an authentication, but should not automatically perform the request at the end of successful authentication. Performing the request would allow security vulnerabilities in form of a more sophisticated form of CSRF attack: For instance, a user could be tricked into deleting all his email just by authenticating. Instead, successful authentication should return a safe fallback page. Protecting against this is called strong CSRF protection (compared to weak csrf protection which only protects against CSRF attacks without user involvement.)

Two parameters govern the behavior regarding strong csrf protection:

/config/security/token/enableStrongCsrfProtection
This configuration parameter determines whether strong csrf protection is enabled (if set to true) or not (if set to false). The default for this parameter is false, as this makes an arbitrary application at least resistant against weak CSRF attacks without breaking it when enabling transparent CSRF protection without any further required configuration. However, you are strongly encouraged to set it to true if possible by defining a safe fallback (see next bullet) and/or suitably defining protected resources as not requiring csrf protection as explained in following section
/config/security/token/csrfGlobalPostLoginFallback
This configuration parameter determines to which page a user is redirected to after successful authentication triggered by an unauthenticated request for a (csrf-)protected resource if strong csrf protection is enabled (otherwise, the user will be redirected to the actual protected resource). The default for this parameter is the root of your server.

Resource Requirements

If you enable CSRF protection, all your protected resources automatically require CSRF credentials on access.

There may be a need to allow some protected URIs to be accessed using a request that lacks the CSRF credential. This may be because the URI represents one of the following (1) it is part of the login process and represents a safe fallback page described previously, (2) it represents a page in the application that a user should be able to bookmark, (3) it is accessed by a page that for some reason cannot include CSRF credentials in the request. In such cases, you can mark resources with csrfProtect=false in the corresponding @include ${zero.core}/config/security/rule.config rule.

Do this only for resources that do not cause server-side change and only for HTML files. For example, doing this on JSON resources can allow JavaScript Hijacking attacks. Typically, you would apply this only to HTML pages forming the entry point into the secure part of your application, containing only the application logic, maybe some additional personalized but non-sensitive information but no confidential resources themselves.

See also general Authorization Considerations.

Example

Putting everything together, you might have a zero.config file, as shown in the following example for securing your application:


# enable CSRF protection, but not transparent code instrumentation as we do it more fine-grained
/config/security/token/enableCsrfProtection="REQUEST"
# Due to above, we also disable ACF by default # (but enable it below for resource access by using merged config files)
/config/acf/enableByDefault=false
# We want strong security, hence we enable StrongCsrfProtections
/config/security/token/enableStrongCsrfProtection=true
# enable transparent rewriting on /todo.gt
@include "acf.config" { "conditions":"/request/path =~ /todo.gt", "contentType" : ["text/html"], "filterRuleFile" : "${/config/dependencies/zero.acf}/config/acf-config.csrf.xml" }
# Transparent authentication triggered by access to csrf-protected resources will fall back on below
/config/security/token/csrfGlobalPostLoginFallback = "/todo.gt"
# ports
/config/http/port=8880 /config/https/port=8883
# SSL setup
/config/https/keyStore="config/myKeyStore.p12" /config/https/keyStorePassword="passw0rd" /config/https/keyStoreType="PKCS12"
# Authentication Method
myAuthType="Form" @include "security/form.config"{ "formLoginPage" : "/login/index.html" }
# allow logouts
/config/handlers += [{ "events" : "POST", "handler" : "zero.core.security.LogoutHandler.class", "conditions" : "/request/path =~ /logout" }]
# todo.gt is the entry-page: We need an authenticated user but this page can be # without CSRF-protection as it does not cause any server changes by # itself and, being html, cannot be read by hijacked by code from a foreign domain # Obviously, being sensitive, we require SSL.
@include "security/rule.config"{ "conditions": "/request/path =~ todo.gt", "authType" : ${myAuthType}, "groups" : ["ALL_AUTHENTICATED_USERS"], "csrfProtect" : false, "requireSSL" : true }
# Security of resources is crucial: they have to be CSRF-protected and ACF-protected
@include "security/rule.config"{ "conditions": "/request/path =~/resources/.*", "authType" : ${myAuthType}, "groups" : ["ALL_AUTHENTICATED_USERS"], "csrfProtect" : true, "requireSSL" : true } @include "acf.config" { "conditions": "/request/path =~/resources/.*", "contentType" : ["text/html"], "filterRuleFile" : "${/config/dependencies/zero.acf}/config/acf-config.default.xml" }
# Scripts such as dojo and CSS can be public but have to be # integrity-protected as they are active content, hence the SSL # requirement
@include "security/rule.config"{ "conditions": "/request/path =~ .*.(js|css)", "requireSSL" : true }
# Images of theme and from dojo are neither secret nor thaat # integrity-sensitive. Hence allow public access
@include "security/rule.config"{ "conditions": "/request/path =~ ((/zero/theme/|/dojo/|/dijit/).*.(jpg|png|gif)|/favicon.ico)", "requireSSL" : false }
# Finally a secure catchall for the rest to be on the safe side ...
@include "security/rule.config"{ "conditions": "/request/path =~ .*", "authType" : ${myAuthType}, "groups" : ["ALL_AUTHENTICATED_USERS"], "csrfProtect" : true }
## derby embedded
/config/db/simpletodo = { "class" : "org.apache.derby.jdbc.EmbeddedDataSource", "databaseName" : "./db/todos.db", "connectionAttributes" :"create=true" }

Best practices

The following list of best practices can maximize the security of your application:

  • Follow the rules of thumb given in the programmatic protection section.
  • Minimize the use of resources with csrfProtect=false and use this only for HTML pages that are entry pages securing parts of the application.
  • Minimize the HTML pages that contain confidential information or trigger server-side state changes and hence must be (CSRF-) protected. HTML pages should ideally contain only the application code and some non-sensitive personalized information; retrieve confidential data and cause critical server-side state-change separately over XHR if possible. This reduces the need for URI query string encoded CSRF-authenticators that easily leak on HTML pages, for example through HTTP Referer fields.
  • If possible, load CSRF-protected HTML pages using client-side instrumentation based on the header encoding. Server-side instrumentation would have to rely on URI query string encoded CSRF-authenticators and induce a high risk to leak the code credentials, for example through HTTP Referer fields.
  • Use the URI-encoding only on resources that you know do not contain references to (untrusted) foreign domains as this will cause the leakage of the CSRF credential. For example, apply the server-side re-writer with the acf-config.csrf.xml rule only on a page that does not contain <A>, <IFRAME>, <FRAME> or <FRAMESET> elements pointing to a resource on your server which in turn references a foreign resources, for example a <IMG>;.

Limitations

  • The CSRF protection is currently limited to a single site. It does not support clustering nor does it protect requests among a realm of different servers, for example, unified behind a reverse proxy.
  • The client-side instrumentation, both transparent and programmatic, has the following limitation. When authentication is over HTTPS, all client-side credentials, including the CSRF credential are stored as secure cookies. In this case, an HTTP page that makes a request to an HTTPS resource cannot include the CSRF credential. So CSRF-protected HTTPS resources cannot be directly accessed from an HTTP page. To work around this limitation, the HTTP page should first transition to a non CSRF-protected HTTPS page (that contains no confidential information), which should then make the CSRF-protected resource requests. (See also the section on Credential Strength.)

Extending WebSphere sMash Security Framework

WebSphere sMash gives you various ways to extend the security framework (refer to the Extending Security section of the Developer's Guide). To make sure that your addition do not break the CSRF protection mechanisms, you have to follow a few rules when adding authentication and authorization handlers as outlined below.

Adding an Authentication Mechanism

During initial authentication, WebSphere sMash will have to create a CSRF credentials and return it to the user. To guarantee that this happens also for new authentication mechanism, it is important that you call TokenService.attachToken().

Furthermore, if you allow for on-demand authentication on (unauthenticated) access to protected resources, you have to also call the methods preparePostSuccessAuthURI and finalizePostSuccessAuthURI of the class CSRFProtectorInternals to deal properly with strong CSRF protection. Please refer to Javadoc of class zero.core.security.CSRFProtectorInternals for more information.

See also the implementation of the methods zero.core.security.BasicSecurityHandler.onBasicSecure() or zero.core.security.FormSecurityHandler.handleFailedTokenValidation() and zero.core.security.form.FormBasedLoginHandler.handleAuthenticationSuccess() for examples on how CSRF protection is integrated.

Adding an Authorization Handler

In case your handler does not only add further access restriction but also does positive authorization, i.e., it returns isAuthorized, it is crucial for the security against cross-site request forgery attacks that you verify CSRF credentials. WebSphere sMash encodes the csrf credential of a request in a CodePrincipal of the subject. To check whether a resource access requires such a credential and to verify its presence your authorization handler should contain code fragment such as the following (assuming authorized is a boolean keeping track of the authorization status of the request.

if (authorized && CSRFProtectorInternals.currentResourceIsCSRFProtected()) {
// resource is csrf protected, so we have to check for presence of csrf credentials
List<String> codePrincipals = GlobalContext.zget(Request.Subject.remoteCodePrincipals, Collections.<String>emptyList()); if (! codePrincipals.contains(CSRFProtectorInternals.getCSRFPrincipal().getName())) { authorized = false; // reset authorized state as request does not contain CSRF credentials } }

Please refer to Javadoc of class zero.core.security.CSRFProtectorInternals for more information. See also zero.core.security.authorization.AuthorizationHandler.onAuthorize() for an example.

Version 1.0.0.3.25591