Updated on 10 May, 202629 mins read 17 views

As we have a proper understanding of the CORS from the last chapter. We know that CORS prevents malicious JavaScript from reading responses, not from sending requests.

This means a CSRF attack can still succeed even on perfectly configured CORS setup.

Understanding the Core Problem

To understand CSRF deeply, you must first understand browser behavior.

Browsers automatically attach:

  • Session cookies
  • Authentication cookies
  • Client certificates
  • Cached credentials

to requests sent to matching domains.

This happens regardless of:

  • Who initiated the request
  • Which website triggered it

Example:

When you log into a website:

https://bank.com

The server gives your browser a session cookie:

Set-Cookie: session=abc123

Your browser stores it automatically.

This is what keeps you logged in.

  • Convenient for users.
  • Dangerous for security.

Now the user visits:

https://evil.com

The attacker hosts:

<img src=https://bank.com/transfer?amount=5000&to=attacker>

The browser automatically sends:

GET /transfer?amount=5000&to=attacker
Host: bank.com
Cookie: session_id=abc123

The bank sees:

  • Valid session cookie
  • Authenticated user

and process the transfer.

The bank cannot distinguish:

  • Legitimate user action

    vs

  • Forged browser request

unless proper CSRF defenses exist.

Why CSRF Exists

CSRF exists because of three browser behaviors:

A. Automatic Credential Inclusion

Browsers automatically send cookies for matching domains.

This convenience feature enables:

  • Persistent logins
  • Sessions
  • Seamless authentication

But it also creates the vulnerability.

B. Cross-Site Requests Are Allowed

Browsers allow websites to trigger requests to other sites using:

  • Forms
  • Images
  • iframes
  • Links

Example:

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

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

The browser allows this request.

C. Servers Trust Cookies Too Much

Many applications assume:

If the request contains a valid session cookie, it must be legitimate.

This assumption is incorrect.

Cookies only prove:

  • The browser is authenticated

They do NOT prove:

  • The user intentionally initiated the request

This distinction is the heart of CSRF.

The Original Web Security Problem

Imagine:

  1. You are logged into your bank
  2. Then you visit

    https://evil.com

Now the malicious website tries to access your bank.

Two dangerous things could happen:

AttackProblem
Read your bank dataPrivacy theft
Send actions as youUnauthorized actions

Browsers needed separate protections for these two problems.

Same Origin Policy and Cross Origin Resource Sharing Solves the “Reading” Problem

Browser introduces:

  • Same-Origin Policy (SOP)
  • later CORS

Their purpose is:

“Can JavaScript from one website read data from another website?”

Example:

fetch("https://bank.com/account")

If this code runs on:

https://evil.com

the browser blocks access to the response.

Why?

Because otherwise malicious sites could steal:

  • bank balances
  • emails
  • private messages
  • personal information

So CORS mainly protects:

Reading responses

NOT sending requests.

Browsers Still Allow Many Cross-Origin Requests

This surprises many developers.

Browsers still allow websites to send many cross-origin requests because the web depends on it.

Examples:

 <img src=https://other-site.com/image.png>
<script src=https://cdn.com/lib.js>
<form action="https://bank.com/pay">

If browsers blocked all cross-origin requests:

  • the web would stop working

So browsers chose this rule:

AllowedBlocked
Sending many requestsReading sensitive responses

And this is exactly why CSRF exists.

What CSRF Really Is

CSRF means:

A malicious website tricks your browser into sending an authenticated request to another wbsite where you are already logged in.

The attacker does NOT steal your cookie.

The browser sends it automatically.

Example CSRF Attack

You are logged into:

https://bank.com

Then you visit:

https://evil.com

The attacker page contains:

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

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

Your browser sends:

POST /transfer
Cookie: session=abc123

The bank sees:

  • valid session cookie
  • authenticated user

and processes the transfer.

Important Thing to Notice

The attacker never:

  • stole your cookie
  • read the response
  • bypassed login

They simply abused browser behavior.

Why CORS Does NOT Stop This

This is where most confusion happens.

People think:

“CORS blocks cross-origin requests.”

No.

CORS maily blocks:

JavaScript reading the response

The request itself is often still sent.

So in the CSRF attack:

  • browser sends the request
  • cookies are included
  • server processes it

Only AFTER that does CORS say:

“evil.com cannot read the response.”

But the damage already happened.

  • Money transferred
  • Password changed
  • Email updates.

That's why:

CORS is NOT CSRF protection.

Simple Requests vs Non-Simple Requests

Browsers classify requests into two types.

Simple Requests

These are considered "normal web behavior".

Examples:

  • GET
  • POST forms
  • Standard form submissions

Allowed content types:

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

Simple requests:

  • are sent immediately
  • include cookies
  • do NOT need preflight

This makes them ideal for CSRF attacks

Why HTML Forms Are Dangerous

HTML forms are limited.

They:

  • cannot send JSON
  • cannot add custom headers
  • cannot use PUT/PATCH easily

But they CAN:

  • send POST requests
  • send cross-origin requests
  • include cookies automatically

And forms use:

application/x-www-form-urlencoded

which qualifies as a simple request.

So browsers send them immediately.

No permission checks first.

When CORS Indirectly Helps

Now comes the subtle part.

Suppose your API only accepts:

Content-Type: application/json

A normal HTML form cannot send JSON.

To send JSON, attacker must user JavaScript fetch:

fetch("/transfer", {
  method: "POST",
  headers: {
    "Content-Type": "application/json"
  }
})

This becomes a NON-simple request.

So browser first sends a:

OPTIONS

preflight request.

Browser asks server:

“Does this server allow cross-orign JSON requests?”

If server says no:

  • browser blocks the real request completely.

So JSON APIs are naturally harder to CSRF.

Anatomy of a CSRF Attack

A CSRF attack typically involves four steps:

Step 1: Victim Logs Into Target Site

Example:

https://bank.com

Server creates:

Set-Cookie: session_id=abc123

The browser stores the cookie.

Step 2: Victim Visits Malicious Site

Example:

https://evil.com

The user may arrive via:

  • Email link
  • Advertisement
  • Social engineering
  • Forum post
  • Phishing message

Step 3: Malicious Site Triggers Request

The malicious page silently submits:

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

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

Step 4: Browser Automatically Includes Cookie

The browser sends:

POST /transfer
Host: bank.com
Cookie: session_id=abc123

The request succeeds because:

  • Cookie is valid
  • User authenticated.

Types of CSRF Attacks

1 GET-Based CSRF

Simplest form.

Example:

<img src=https://bank.com/delete-account>

When the browser loads the image:

  • Request is sent automatically

Dangerous if state-changing actions use GET.

Why GET Requests Should Never Change State

HTTP standards define:

  • GET = safe and idempotent
  • POST/PUT/DELETE = State-changing

Violating this principle creates major CSRF risk.

Bad:

GET /delete-user?id=10

Good:

POST /delete-user

2 POST-Based CSRF

More common and dangerous.

Uses hidden forms:

<form action="https://bank.com/change-password" method="POST">
  <input type=hidden name=password value="hacked123">
</form>

Auto-submitted via JavaScript.

3 JSON CSRF

Modern APIs may accept JSON requests.

Attacker abuse:

  • Weak content validation
  • Browser quirks
  • Misconfigured APIs

Example:

fetch("https://api.bank.com/transfer", {
  method: "POST",
  credentials: "include",
  body: JSON.stringify({...})
})

Real World Example

Suppose

https://socialmedia.com/change-email

requires only:

POST /change-email
Cookie: session=abc123

Attacker creates:

<form action="https://socialmedia.com/change-email" method="POST">
  <input type=hidden name=email value="attacker@mail.com">
</form>

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

Victims visits attacker page.

Result:

  • Email changed
  • Account takeover possible
Buy Me A Coffee

Leave a comment

Your email address will not be published. Required fields are marked *