|
|
|
Cross site request forgery
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. The following sections of this article describe cross-site request forgery and how to prevent it.
How it works
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 to cause server-side actions on the user's behalf and, to a lesser extent, also retrieve confidential data, for example, from JSON requests using a special form of CSRF called
JavaScript Hijacking attacks.
The attack works for all applications relying exclusively on traditional Web-authentication mechanism such as session cookies or HTTP basic authentication when no special precautions are taken. Hence, these attacks are very powerful and
OWASP lists them in the Top 10 Web application vulnerabilities. For more information see this Wikipedia article.
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, Project Zero provides corresponding
countermeasures built into the framework.
Enabling CSRF protection
To enable protection, you must perform the following steps:
- Instrument your HTML and JavaScript files to include, in the server request, a CSRF-authenticator that assures the framework the request comes from your client-side code and not from a rogue application running in the client's browser.
- Modify the
zero.config configuration file to enable CSRF-protection and to specify which resources require CSRF protection and which pages are safe entry points into the application.
Code instrumentation
CSRF protection relies on proof that a request for a protected server-side resource or action originates from an authenticated user's browser and from your own code. This code is loosely defined as the union of HTML, JavaScript code and CSS. That is, CSRF protection requires a form of code-credentials. Hence, each of your code pieces referencing protected server resources has to be instrumented to provide such proofs to the server with each request.
Depending on the particular application requirement, there are two, not necessarily mutually exclusive, modes of operation:
- Transparent Protection
- 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 Protection
- 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 protection
The most convenient way to instrument applications is to rely on the transparent rewriting of HTML files on the server-side and client-site 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 Zero. After including the ACF library and resolving the ACF dependency for your application, add 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 Project Zero. See the conditions operations information of the Configuration article. You should also consult the general best practices for configuring security rules information in the Security considerations article as these also apply.
-
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-csrf-config.xml - Rewrites all references to server-resources.
-
${/config/dependencies/zero.acf}/config/acf-csrf-safe-config.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-csrf-config.xml"
}
For more information on leakage through HTTP referer fields, see the Best practices section.)
Notes:
- 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 need to manually merge the CSRF-protection configuration
${/config/dependencies/zero.acf}/config/acf-csrf-config.xml with the ACF rules 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. Based on
acf-csrf-config.xml or acf-csrf-safe-config.xml, 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.0), 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.
Note
- 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 protection
The methods described in the previous section should suffice for typical programs and are fail-safe to use. However, the rewriting might not meet your performance goals 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:
- HTTP Header
- Parameter in form
- 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)
See the JavaDoc
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)
See the Using Project Zero Cross Site Request Forgery (CSRF) APIs programmatically section of the Additional PHP functions article for more information.
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
To enable CSRF protection, add /config/security/token/enableCsrfProtection=true to your zero.config file
As authentication from a user perspective rarely implies authorizing immediate destructive operations, implicit authentication on a CSRF-protected resource does not trigger the requested resource but returns a safe fallback page. This prevents CSRF-style attack on the integrity of user data (for example, in an email application a user could otherwise be tricked to delete all his email just by authenticating). The fallback can be specified assigning the property
/config/security/token/csrfGlobalPostLoginFallback in your zero.config file with your desired fallback page.
If you enable CSRF protection, all your protected resources automatically require CSRF credentials on access. If you have to allow some protected resources to be accesses by code that because it can either not be instrumented to issue CSRF credentials for each request or because it is part of the login process where the user does not yet have CSRF credentials, you can mark resources with csrfProtect=false in the corresponding [@include ${zero.core}/config/security/rule.config]
rule.
Note: 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 the HTML pages forming the entry point into the secure part of your application, containing only the application logic but not the confidential resources themselves.
Putting everything together, you might have a zero.config file, as shown in the following example, for securing the
simpletodo application from the tutorial:
/config/security/token/enableCsrfProtection=true
# enable transparent rewriting on /todo.gt
@include "acf.config" {
"uri":"/todo.gt",
"filterRuleFile" : "${/config/dependencies/zero.acf}/config/acf-csrf-config.xml"
}
# Transparent authentication triggered by access to csrf-protected resources will generally 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
@include "security/rule.config"{
"conditions": "/request/path =~/resources/.*",
"authType" : ${myAuthType},
"groups" : ["ALL_AUTHENTICATED_USERS"],
"csrfProtect" : true,
"requireSSL" : true
}
# Scripts such as dojo and CSS can be public but have to 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:
- Minimize the use of resources with
csrfProtect=false and use this only for HTML pages that have 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 contain only the application code and retrieve confidential data and cause critical server-side state-change only using XHR. 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-csrf-config.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.
|