Cross-Site Request Forgery (CSRF) Attacks

Posted February 5, 2016 - 6 min read
Topics:  

Cross-Site Request Forgery (or simply CSRF, pronounced sea surf) is web security vulnerability which makes use of the trust, that a given web application has to an authenticated user, to perform a tricked action on behalf of that user.

The result of a CSRF attack may be disastrous for the user as well as for the web application.

For example, considering that a user has logged into his bank account and is maintaining an active session, an attacker may trick the user to click a link which may be sent to him by email/SMS or simply a link on a web page that the attacker controls.

Once the user clicks that link, the attacker, for example, may initialize a money transfer or change the user’s password.

Within this post we are going to discuss CSRF attacks and explore some ways to prevent them.

Overview

Usually CSRF involves a web application that holds and protects data which belongs to a user. The user has to be authenticated to have granted access to.

Successful CSRF attacks have to gain access to protected user data by sending requests on his behalf and without his awareness.

Common CSRF rely on, but not limited to, the fact that each time the browser send an HTTP request to the server, as long as the request parameters match those defined in the cookies (path, domain, httponly, secure, expiration, etc.) the cookies will be also sent.

Usually a web application validates an active session by verifying if the user requests contain a session cookie which was previously placed into the browser upon successful authentication.

We can say that the main trait of all CSRF attacks is about tricking a trusted user to perform some action which allows the attacker to forge malicious requests that are validated by the server as valid requests.

Example

Considering that we have a vulnerable web application that allows users to change their passwords using the following form:

<form id="password_form" method="POST" action="http://myawesomebank.com/account/password">
  <input type="password" name="password" />
  <input type="submit" value="Save" />
</form>

An attacker may send to his victim a malicious email pretending to be from his bank and asking the victim to open a link to view a special offer.

The page associated with the link is hosted on a web server that is controlled by the attacker and contains a modified version of the original form:

<form id="change_password" method="POST" action="http://myawesomebank.com/account/password">
  <input type="password" name="password" value="789456123" />
  <input type="submit" value="Save" />
</form>
<script>
  document.getElementById('password_form').submit();
</script>

When the victim visits the page, a POST request for changing the victim’s password will be sent.

Of course, for this CSRF attack to be successful the victim must have an active session.

How to prevent CSRF attacks?

Using CSRF Tokens

Also known as anti-CSRF tokens.

This is the most popular approach to prevent CSRF attacks.

A unique and unpredictable token must be included for each request that may change the application state to ensure that the request has been sent legitimately.

The client must obtain an anti-CSRF token from the server before sending a sensitive request.

The server stores the anti-CSRF token in the user session.

To verify that a request is legitimate, the server compares the token from the request with the token stored in the user session.

The request is rejected for unmatched tokens.

Stateless CSRF Tokens

For stateless applications, without a session storage, the server may generate an anti-CSRF token containing the session ID (JWT token) and a timestamp and encrypt it using a secret key.

The lifespan of this token is limited for a certain period of time.

Before sending a sensitive request the client obtain an anti-CSRF token from the server and includes it in the request.

When a sensitive request is received, the server looks for an anti-CSRF token, decrypts it using the secret key, and checks if the session ID from the token matches the session ID from the request.

Additionally, the timestamp of the token is also validated.

The request is rejected for unmatched session Ids and expired timestamps.

This approach, which is known as the Double Submit Cookie Pattern, consists of generating a strong random value, a CSRF token, and storing it in a cookie whenever an application authenticates a user.

The server itself does not store such token.

When the client sends a sensitive request, it includes the token in the request parameters, in a hidden field within a form, or in an HTTP Header.

To validate the request, the server checks if the token from the cookie matches the token from the request and the request is rejected if the tokens are unmatched.

This approach relies on the fact that the token set in the cookie can not be read or modified by the attacker from a page he controls.

The browser does not allow JavaScript, on a given page, to read cookies belonging to other pages from other origins (the Same Origin Policy).

Validating the Origin and Referer HTTP headers

Both Origin and Referer HTTP headers are considered as forbidden headers.

The browser does not allow the client to modify a forbidden header with JavaScript.

So the application server may prevent a CSRF attack by validating the source origin and the target origin of a request.

The request is rejected for unmatched origins.

Using Custom HTTP Headers

Another approach is based on the presence of custom HTTP headers within sensitive XHR requests.

The same origin policy (SOP), which is implemented by all browsers, does not allow JavaScript to set custom HTTP headers when sending requests to other origins.

To validate a request, the server checks if the request has a custom header (for example an X-CSRF-Random header). The value of such header is irrelevant.

The request is rejected if such custom header does not exist.

While this approach achieves a sufficient defence against CSRF attacks, it is recommended to combine it with other approaches as a matter of increasing the protection.

This approach works only in secure contexts (HTTPS requests) and restricts the browser to send cookies along with requests only when such requests are being sent from the first-party context (from the same origin context).

It can be achieved using Set-Cookie HTTP response header by setting the SameSite attribute of Set-Cookie to Strict:

Set-Cookie: <cookie-name>=<cookie-value>; SameSite=Strict

This approach is a sufficient defence against CSRF attacks when cookies are used to store session ID as well as other sensitive information.

To increase the protection against CSRF attacks it should be combined with other approaches.

Enforcing the user to Re-authenticate

Sometimes for critical operations, you may require the user to authenticate himself again before the request is sent.

This is a high level of protection, and you should only use it for some critical operations as using it for every sensitive request is counter-productive and breaks the user experience.

Conclusion

Within this post we have seen what are CSRF attacks and discussed various approaches to prevent them.

However, you should not forget to protect your application against XSS vulnerabilities which may allow the attacker to bypass many of protection mechanisms.