English
  • csrf attack
  • web security
  • cross-site request forgery
  • cookie security
  • same-origin policy
  • csrf prevention
  • SameSite

Understanding CSRF in depth

Provides an in-depth exploration of Cross-Site Request Forgery (CSRF) attacks, explaining their mechanics, demonstrating examples, and detailing various prevention methods to enhance web application security.

Yijun
Yijun
Developer

When working on web development, especially with cookies, we often hear phrases like "this setting helps prevent CSRF". However, many people only have a vague idea of what "CSRF" really means.

Today, we'll take a deep dive into CSRF (Cross-Site Request Forgery), a common web security flaw. This will help us handle CSRF-related issues more effectively.

What is CSRF?

CSRF (Cross-Site Request Forgery) is a type of web attack where attackers trick authenticated users into performing unintended actions. In simple terms, it's "hackers pretending to be users to carry out unauthorized actions".

How CSRF works

To understand CSRF, we need to grasp a few key concepts:

Browser's Same-Origin policy

The same-origin policy is a security feature in browsers that limits how a document or script from one origin can interact with resources from another origin.

An origin consists of a protocol (like HTTP or HTTPS), domain name, and port number. For example, https://example.com:443 is one origin, while https://demo.com:80 is another.

The same-origin policy restricts data access between pages from different origins, meaning:

  • JavaScript from one origin can't read the DOM of another origin
  • JavaScript from one origin can't read the Cookie, IndexedDB, or localStorage of another origin
  • JavaScript from one origin can't send AJAX requests to another origin (unless using CORS)

However, to maintain the openness and interoperability of the Web (like loading resources from CDNs or sending requests to third-party APIs for logging), the same-origin policy doesn't restrict cross-origin network requests:

  • Pages can send GET or POST requests to any origin (like loading images or submitting forms)
  • Resources from any origin can be included (like <script>, <img>, <link>, <iframe> tags)

The automatic cookie sending mechanism is an important feature of browsers. When a browser sends a request to a domain, it automatically attaches all cookies for that domain. This process is automatic and doesn't require any JavaScript code or user interaction.

This mechanism allows websites to easily remember users' login status because each request automatically carries the user's identity information. Bold For example, when you log into a bank website (bank.com) and get an identity cookie, then when you click to view your statement, the browser automatically finds all cookies matching bank.com and attaches them to the statement request. The bank's server can then identify you from the backend and return your statement information.

CSRF attack steps

  1. The user logs into the target website (like a bank site) and gets an authentication cookie. This step uses the automatic cookie sending mechanism. After the bank site sets an identity authentication cookie, the browser will automatically attach this cookie to every request sent to that site.

  2. Without logging out, the user visits a malicious website. At this point, due to the same-origin policy, the malicious site can't directly read or modify the bank site's cookie. This protects the user's identity information from being directly stolen.

  3. The malicious site includes a request to the target site (like a transfer operation). Although the same-origin policy restricts cross-origin access, it allows cross-origin network requests, such as requests initiated through <img>, <form> tags. Attackers exploit this "loophole".

  4. The user's browser automatically sends this request, along with the target site's cookie. This is the core of the CSRF attack. It takes advantage of both the same-origin policy allowing cross-origin requests and the automatic cookie sending mechanism (even requests triggered by malicious sites will carry cookies matching the domain).

  5. The target site receives the request, verifies the cookie is valid, and executes the operation. The server can't tell if this request comes from a legitimate user action because the attached cookie is valid.

CSRF attack example

Let's illustrate how a CSRF attack happens with a specific example. We'll use a fictional bank website bank.com as an example.

First, the user visits https://bank.com and logs into their account.

After successful login, the server sets an authentication cookie, for example:

The user performs a transfer operation on the bank website, say transferring $1000 to Alice. This operation might send a request like this:

Now, suppose an attacker creates a malicious website https://evil.com containing the following HTML:

When the user clicks the https://evil.com link without logging out of their bank account, because they're already logged into bank.com, the browser has a valid session_id cookie.

After the evil page loads, it automatically submits the hidden form, sending a transfer request to https://bank.com/transfer.

The user's browser automatically attaches the bank.com cookie to this request. The bank.com server receives the request, verifies the cookie is valid, and then executes this unauthorized transfer operation.

Common methods to prevent CSRF attacks

Here are several commonly used CSRF defense methods. We'll explain in detail the principle of each method and how it effectively prevents CSRF attacks:

Using CSRF tokens

CSRF tokens are one of the most common and effective methods to defend against CSRF attacks. Here's how it works:

  1. The server generates a unique, unpredictable token for each session.
  2. This token is embedded in all forms for sensitive operations.
  3. When the user submits a form, the server verifies the validity of the token.

Because the CSRF token is a unique value bound to the user's session, and the attacker can't know or guess this value (as it's different for each session), even if the attacker tricks the user into sending a request, the request will be rejected by the server due to the lack of a valid CSRF token.

Implementation example:

On the server side (using Node.js and Express):

In frontend JavaScript:

Checking Referer header

The Referer header contains the URL of the page that initiated the request. By checking the Referer header, the server can determine if the request comes from a legitimate source.

Since CSRF attacks usually come from different domains, the Referer header will show the attacker's domain name. By verifying whether the Referer is the expected value, requests from unknown sources can be blocked.

However, it's worth noting that this method is not entirely reliable, as some browsers might not send the Referer header, and users can disable the Referer header through browser settings or plugins.

Implementation example:

SameSite is a cookie attribute used to control whether cookies are sent with cross-site requests. It has three possible values:

  • Strict: Cookies are only sent in same-site requests.
  • Lax: Cookies are sent in same-site requests and top-level navigation.
  • None: Cookies are sent in all cross-site requests (must be used with the Secure attribute).

When SameSite is set to Strict, it can completely prevent third-party websites from sending cookies, thus effectively preventing CSRF attacks. If SameSite is set to Lax, it protects sensitive operations while allowing some common cross-site use cases (like entering a website from external links).

Implementation example:

Using custom request headers

For AJAX requests, custom request headers can be added. Due to the restrictions of the same-origin policy, attackers cannot set custom headers in cross-origin requests. The server can check for the presence of this custom header to verify the legitimacy of the request.

Implementation example:

On the frontend:

On the server side:

Double cookie verification is an effective CSRF defense technique. Its core principle is that the server generates a random token, sets it as both a cookie and embeds it in the page (usually as a hidden form field). When the browser sends a request, it automatically includes the cookie, while the page's JavaScript sends the token as a request parameter. The server then verifies if the token in the cookie matches the token in the request parameters.

Although attackers can include the target website's cookie in cross-site requests, they cannot read or modify the cookie's value, nor can they access or modify the token value in the page. By requiring the request to include tokens from both the cookie and parameters, it ensures the request comes from a source with permission to read the cookie, thus effectively defending against CSRF attacks.

Using re-authentication for sensitive operations

For particularly sensitive operations (like changing passwords or making large transfers), users can be required to re-authenticate. This provides users with an additional security checkpoint. Even if a user successfully initiates a CSRF attack, they cannot pass the re-authentication step.

Implementation suggestions:

  • Before performing sensitive operations, redirect to a separate authentication page.
  • On this page, require the user to enter their password or other identity verification information.
  • After verification passes, generate a one-time token and use this token in subsequent sensitive operations.

Summary

Through this in-depth discussion, we hope you now have a more comprehensive understanding of CSRF attacks. We've not only learned about how CSRF works but also explored various effective defense measures. All these methods can effectively enhance the security of web applications.

Hope this knowledge will help you better handle CSRF-related issues in your daily development and build more secure web applications.