Product SiteDocumentation Site

3.1.3. Cross site request forgery (CSRF)

By default, browsers include user's authentication tokens (such as cookies, HTTP basic authentication credentials etc.) with every request to the web application. This allows client to authenticate once and each following request to the web application will be authenticated without prompting the user for credentials. However, this gives client's browser ability to make authenticated requests on behalf of the user without user's explicit consent.
This behaviour can be misused by the attacker to confuse client's browser into issuing an authenticated request. For example, if attacker's website contains this simple script tag
<script src="http://victimbank.com/transfermoney?to=attacker&amount=1000"/>
browser will issue a HTTP GET request to victimbank.com with parameters supplied by the attacker. The browser does not know anything about the resource that is being requested by the attacker's site - whether it is malicious of harmless - and it requests the script from the specified URL. If the user is authenticated at that moment, browser will also include his credentials, so the request would look like this:
GET /transfermoney?to=attacker&amount=1000 HTTP/1.1
Host: victimbank.com
Cookie: ...
Even though browser believes it is asking for a resource, web application will perform action specified in the request from the client - in this case, send money to the attacker. Such web application is vulnerable to Cross Site Request Forgery.

Important

Web application should not change state or perform security sensitive actions upon receiving HTTP GET requests. Such behaviour is not compliant with HTTP and may create problems with caches, browser prefetching etc.
It is not enough to make sure that web application does not use HTTP GET requests to perform security sensitive actions - it is important that such requests are forbidden by the application. For example, Rails application's action can be invoked only with non-GET requests throughout the application, but still be routable through GET requests.
Restricting security-sensitive operations to non-GET requests does not protect from CSRF attack itself. Even though common HTTP tags like <img>, <script> and others can be used to issue HTTP GET requests, there are other means to issue arbitrary requests against vulnerable application.
As example consider the code below:
<body onload="document.getElementById('f').submit()">
  <form id="f" action="http://victimbank.com/transfermoney" method="post" name="form1">
    <input name="to" value="attacker">
    <input name="amount" value="1000">
  </form>
</body>
If user visits page containing a code similar to this one, upon loading the page browser will send a HTTP POST request with the parameters supplied by the attacker.
There are several mechanisms available, that allow web application to identify requests issued by a third-party web page from the client's browser.

3.1.3.1. Synchronizer token pattern

OWASP recommended method of CSRF protection is to include a challenge token in each sensitive request. The token must be unpredictable to the attacker, otherwise the attacker could guess it and include with his forged request. The token must also be tied to user's session - if the token is shared by users, they would be able to forge requests on behalf of others. It goes without saying that it cannot be part of the authentication tokens, since they are sent with each request automatically, which defeats the purpose of CSRF protection. However, this token needs to be generated only once per each session.
The CSRF challenge token should be included in all non-GET requests, including Ajax requests. On the server side, application has to verify the token is included in request and is valid, and reset session otherwise.
Synchronizer token pattern is also default CSRF protection mechanism for Rails applications. To enable CSRF protection, one has to enable it in application controller with
protect_from_forgery
which will automatically include CSRF token in all non-get and XHR requests. The token itself is sent by the server in meta tag of the web page like this:
<meta content="authenticity_token" name="csrf-param" />
<meta content="VBlgpnibfsxm1QykEmlOCbxqLRxx7kDGr57tjE+LLZk=" name="csrf-token" />
If the request is not verified to be CSRF-free, Rails resets the session by default:
def handle_unverified_request
  reset_session
end
If this does not effectively log out user due to application-specific behaviour, developers should redefine handle_unverified_token.
The disadvantage of synchronizer token pattern is the need to rememnber the challenge token for each session on the server side.

3.1.3.2. Double submit cookie pattern

This method mitigates the problem of keeping state on the server side. Each sensitive request shall include a random value twice: in cookie, and as a request parameter. After receiving request, server verified that both values are equal, so this mechanism is stateless.
Assuming the random value meets the requirements on CSRF token, attacker cannot forge the CSRF requests. To do that, he would need an access to random value stored in a cookie of another site, which is prevented by Same Origin Policy.
This mechanism is arguably less secure than synchronizer token pattern. While it is hard for the attacker to read the random value from cookie, it is easier to write a value, for example by writing an attacker-specified value from a subdomain.

3.1.3.3. Encrypted token pattern

Another stateless approach leverages encryption. The token sent by the server is triple User ID, Timestamp and Nonce, encrypted with server-side secret key. The token sent to the client in a hidden field, and returned by the client in a custom header field for Ajax requests or as a parameter for form-based requests.
Validation of token does not require any state on the server side aside from secret key. Upon receiving request, server decrypts the token and verifies User ID against session's User ID (if there is one) and Timestamp to prevent replay attacks. If decryption of the token yields malformed data or any of the checks fails, server blocks the potential attack.

3.1.3.4. Checking Referer header

Checking the Referer header to make sure that request does not originate from the third party site is a common stateless CSRF protection mechanism. Even though it is possible for the user to spoof referer header, it is not possible for the attacker in case of CSRF, since the Referer header is included by the client's browser and outside of attackers control.
Even though it may seem to be the easiest mechanism to implement, it carries a lot of cornercases, depends on configuration outside of applications control and is prone to compatibility issues.
One of the problems of Referer header is potential disclosure of private information, due to which some users may configure their browsers to not include Referer header at all. Referer header is also omitted when browsing from HTTPS secured site to HTTP. Since attacker can mount attack from HTTPS protected page, web application has to deny requests without Referer header. This affects compatibility - for example, when user directly types the URL (or bookmarks it), Referer header will be empty and the application will the refuse request due to CSRF protection, creating usability problems.
From implementation standpoint, CSRF check needs to make sure that request originated from a page from trusted domain, however path with parameters do not matter. It is therefore tempting to implement the check by verifying that Referer start with the domain, ignoring the rest of the path. For example, if the Referer is "http://application.domain.com/some/page", the check would verify that it starts with "http://application.domain.com" and allow the request. This can be bypassed if the attacker mounts CSRF attack from "http://application.domain.com.evil.io".

Important

Checking the Referer header as CSRF protection mechanism is highly discouraged.

3.1.3.5. References