Updated on 10 May, 202619 mins read 23 views

Problem?

Browser automatically carry your identity with requests:

  • cookies
  • login sessions
  • saved credentials
  • authenticaion headers

Without restrictions, any website you visited could use your browser as a “trusted agent” to interact with other sites where you were logged in.

Example:

  1. You log into your bank in one tab.
  2. Later, you visit a malicious website.
  3. That malicious site silently sends requests to the bank using your browser.
  4. Because your browser automatically includes your bank cookies, the bank thinks you made the request.

Thus the malicious page could also:

  • read the bank's responses
  • steal account data
  • perform actions as you

This became a huge security issue as the web evolved from static documents into interactive applications.

What exactly is an “origin”?

An origin is:

scheme + host + port

Examples:

URLOrigin
https://example.com(https, example.com, 443)
http://example.comdifferent origin
https://api.example.comdifferent origin
https://example.com:8080different origin

Two pages are considered “same-origin” only if all three match.

The attack that changed everything

A classic example:

Cross-site data theft

Imagine:

  • You are logged into:
    • email
    • banking
    • corporate intranet

Then you visit:

evil.com

Then this website could do as follows:

const data = fetch("https://bank.com/account");
console.log(data);

The malicious site could:

  • read your account balance
  • extract private messages
  • steal CSRF tokens
  • impersonate you

The browser itself became the attack vector.

Why cookies made this dangerous

Browsers were designed for convenience:

  • automatic login persistence
  • automatic cookie sending
  • seamless navigation

So the browser would attack authentication automatically:

Cookie: sessionid=abc123

Servers trusted these cookies.

Thus any site could abuse that trust.

Same-Origin Policy

SOP introduced isolation boundaries between web applications.

A page from:

https://evil.com

cannot freely:

  • read DOM data from bank.com
  • access JS variables form bank.com
  • inspect responses from bank.com
  • manipulate sensitive cross-origin resources

This transformed the browser into a safer multi-application platform.

Important nuance: SOP does NOT block all requests

A page can still send many cross-origin requests.

For example:

  • loading images
  • loading scripts
  • submitting forms

But SOP mainly prevents:

  • reading sensitive responses
  • direct cross-origin DOM access

That distinction exists because the web depended heavily on cross-site resource loading.

Problems SOP introduced

SOP solved security issues, but created developer friction.

Modern apps often need:

  • APIs
  • CDNs
  • third-party services
  • microservices

So browser later added controlled exceptions:

  • CORS
  • postMessage
  • JSONP (older workaround)
  • Cross-Origin Resource Sharding headers
  • sandboxed iframes

SOP fixed security, but it was too restrictive for modern applications.

The web evolved into distributed systems.

Frontend and backend often live on different origins:

Frontend:
https://myapp.com

API:
https://api.myapp.com

According to SOP:

  • these are different origins
  • browser blocks frontend from reading API responses.

Example problem:

Frontend:

fetch("https://api.myapp.com/users")

Browser error:

Blocked by Same-Origin Policy

Even though both servers belong to the same company.

Why this became painful

Modern apps need cross-origin communication for:

  • APIs
  • CDNs
  • authentication providers
  • payment gateways
  • microservices
  • cloud storage

SOP became too strict for legitimate user cases.

The Solution: CORS

Browsers introduced CORS (Cross-Origin Resource Sharing).

CORS allows servers to explicitly say:

“I trust this other origin.”

How CORS works

Suppose frontend:

https://app.com

calls:

https://api.com

The browser sends:

Origin: https://app.com

The server responds:

Access-Control-All-Origin: https://app.com

The browser checks:

  • Did the server approve this origin?

If yes:

  • browser allows JavaScript to read the response

If not:

  • browser blocks access

Important Idea

CORS is enforces by the browser.

The server only declares permission.

The browser decides whether frontend JS can access the response.

What Actually Happens

In a CORS failure, the browser usually still sends the HTTP request to the server, but it blocks JavaScript from accessing the response.

Suppose your frontend:

fetch("https://api.com/data")

runs from:

https://app.com

The browser sends:

GET /data
Origin: https://app.com

Now two possibilities:

Case 1: Server allows it

Server responds:

Access-Control-Allow-Origin: https://app.com

Browser says:

“Okay, this origin is trusted.”

Then JavaScript gets the response:

const data = await response.json();

works normally.

Case 2: Server does NOT allow it

Server responds without proper CORS headers:

(no Access-Control-Allow-Origin)

The browser still receives the response internally.

But the browser says:

“JavaScript from app.com is not allowed to read this.”

So JS gets:

CORS error

and access is blocked.

Extremely important point

CORS is not server-side security.

It is a browser-enforced reading restriction.

The server may have fully processed the request.

This Surprises many developers

People often think:

“CORS blocked my API call.”

Usually not true.

Most of the time:

  • request WAS sent
  • server MAY have executed it
  • browser simply hid the response from JS

Example: dangerous side effect

Imagine:

fetch("https://bank.com/transfer", {
  method: "POST",
  body: JSON.stringify({
    amount: 1000
  })
})

Even if CORS fails:

  • the request may still reach the server
  • money transfer could still happen

Browser only blocks:

response.text()

That's why:

  • CORS is NOT protection against CSRF
  • separate CSRF protections are needed
Buy Me A Coffee

Leave a comment

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