Cross-Site Request Forgery (CSRF)

·5 min read

Cross-Site Request Forgery (CSRF)

Cross-Site Request Forgery (CSRF) is a web security vulnerability where an attacker tricks a user’s browser into sending a request to another website where the user is already authenticated.

If the server cannot distinguish the forged request from a legitimate one, it executes the requested operation using the victim’s identity.

Why CSRF Is Possible

HTTP is a stateless protocol. To recognize users across multiple requests, web applications usually rely on cookies.

When the browser sends a request, it automatically includes credentials that belong to the destination website, such as:

  • Cookies
  • HTTP Basic or Digest authentication credentials
  • Client TLS certificates

The browser does this automatically. The application does not need to attach these credentials manually.

This behavior is convenient, but it also makes CSRF possible.

How the Attack Works

Imagine a user is logged into bank.example.com.

Later, the same user visits evil.example.

The attacker’s page contains a form like this:

<form action="https://bank.example.com/transfer" method="POST">
    <input type="hidden" name="amount" value="1000">
    <input type="hidden" name="recipient" value="attacker">
</form>

<script>
    document.forms[0].submit();
</script>

When the browser submits the form:

  1. It creates a request to bank.example.com.
  2. It automatically attaches the user’s authentication cookies.
  3. The server receives what appears to be a legitimate authenticated request.
  4. If no CSRF protection is implemented, the transfer is executed.

The attacker never steals the user’s session. Instead, they abuse the browser’s automatic authentication behavior.

Why the Same Origin Policy Doesn’t Prevent CSRF

A common misconception is that the Same Origin Policy prevents CSRF.

It does not.

The Same Origin Policy prevents JavaScript running on one origin from reading data that belongs to another origin. For example, it prevents an attacker’s script from reading:

  • Cookies from another origin
  • The page content
  • The response body

However, it does not prevent the browser from sending requests to another origin.

This distinction is exactly why CSRF attacks are possible.

At the same time, it is also why CSRF tokens work. An attacker can force the browser to send a request, but cannot read the CSRF token generated by the legitimate application.

Same Origin vs Same Site

These two concepts are often confused.

An origin consists of:

  • Scheme (https)
  • Host (app.example.com)
  • Port (443)

For example:

  • https://app.example.com
  • https://admin.example.com

These are different origins.

A site consists of the scheme and the registrable domain.

For example:

  • https://app.example.com
  • https://admin.example.com

These are different origins but the same site.

The SameSite cookie attribute uses the concept of site, not origin.

CSRF Tokens

The most common defense against CSRF is a CSRF token.

The server generates a cryptographically secure random value and sends it to the client.

Whenever the client performs a state-changing request, it sends this token back to the server.

The server verifies that the received token matches the expected value.

An attacker cannot forge a valid request because they cannot read the token due to the Same Origin Policy.

Synchronizer Token Pattern

In the Synchronizer Token Pattern:

  1. The server generates a CSRF token.
  2. The token is stored on the server, usually in the user’s session.
  3. The server sends the token to the client.
  4. The client includes the token in every state-changing request.
  5. The server compares the received token with the one stored in the session.

If they match, the request is accepted.

The Double Submit Cookie Pattern avoids storing the token on the server.

Instead:

  1. The server generates a CSRF token.
  2. The token is sent in a cookie.
  3. JavaScript reads the cookie and copies the value into a custom request header.
  4. The server compares the cookie value with the header value.

If both values match, the request is considered valid.

Since JavaScript must read the cookie, this cookie cannot use the HttpOnly attribute.

SameSite Cookies

The SameSite cookie attribute reduces the risk of CSRF by controlling when cookies are sent.

SameSite=Strict

Cookies are sent only for requests originating from the same site.

Cross-site requests do not include the cookie.

This provides the strongest protection but may negatively affect user experience, for example when users follow links from emails.

SameSite=Lax

Cookies are sent for same-site requests and top-level cross-site navigation using safe methods such as GET.

They are not sent for cross-site POST, PUT, PATCH, or DELETE requests.

This is a common default because it balances usability and security.

SameSite=None

Cookies are always sent, including cross-site requests.

This option must be combined with the Secure attribute.

Simple Requests and CORS

Browsers classify some requests as simple requests.

Examples include:

  • GET
  • HEAD
  • POST

using simple content types such as:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

Simple requests do not require a CORS preflight request.

If JavaScript sends a request with:

  • Content-Type: application/json
  • a custom header such as X-CSRF-Token

the browser performs a CORS preflight request first.

The browser sends an OPTIONS request asking whether the server allows the actual request.

If the server does not explicitly allow the origin, method, and headers, the browser blocks the request before sending it.

This is why many APIs require custom headers or application/json for state-changing operations.

Additional Protection

CSRF tokens should not be the only defense.

Modern applications often combine multiple techniques:

  • CSRF tokens
  • SameSite cookies
  • Origin header validation
  • Referer header validation
  • Requiring non-simple requests for APIs

Using several independent protections provides defense in depth.

CSRF and XSS

Cross-Site Scripting (XSS) can often bypass CSRF protection.

If an attacker can execute JavaScript on your website, that script runs with the same origin as your application.

It can read CSRF tokens, send authenticated requests, and perform actions on behalf of the user.

For this reason, preventing XSS is just as important as preventing CSRF.

Best Practices

If your application uses cookie-based authentication:

  • Use CSRF tokens for every state-changing request.
  • Set the SameSite attribute appropriately.
  • Validate the Origin or Referer header when possible.
  • Never allow GET requests to modify server state.
  • Consider requiring custom headers or application/json for API endpoints.
  • Keep your application protected against XSS.

Conclusion

CSRF exists because browsers automatically attach authentication credentials to requests.

The Same Origin Policy does not prevent these requests from being sent, but it prevents attackers from reading sensitive data such as CSRF tokens.

Modern applications should combine CSRF tokens, SameSite cookies, Origin validation, and secure API design to protect users against forged requests.

Related Posts