What Is Same Origin Policy
When looking to implement a new chunk of JavaScript or exploring credential exfiltration for bug bounty, we definitely run into CORS (Cross-Origin Resource Sharing). In order to understand CORS, it is important to understand Same-Origin Policy (SOP). This core security feature, built into every browser, controls how scripts or documents from one origin can interact with resources from another. It’s a critical element in preventing malicious attacks like Cross-Site Scripting (XSS) and data theft.
But how does SOP really work? How does it determine ‘same origin’? And what implications does it have on the functionality and security of your web applications? Let’s dig in.
- Resources: https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy
- The Web Origin Concept – RFC 6454: https://www.rfc-editor.org/rfc/rfc6454
CORS will be explored deeper in subsequent posts.
Setup
To demonstrate the concepts, I am going to be using the following domains/origins:
https://jhgfdsa.com/soptestpage.html
- Cloudflare Pages
https://api.jhgfdsa.com
- Cloudflare Worker that returns a JSON object with random GUID
https://jhgfdsa.com/api
- Cloudflare Worker that returns a JSON object with random GUID
https://hydrohousehold.com
Note: I keep the content for these domains gated as I do wacky testing on them. As such, they may or may not be online (or at least accessible) and will most likely not have the content as represented in this blog post as I have most likely torn them down to do some other wacky experiment.
Understanding the Origin
What exactly is an “origin”? In web terms (and defined in the RFC), an origin is defined by the combination of a URL’s scheme (protocol), hostname, and port. This trio determines the “address” of web resources and establishes the boundaries for the Same-Origin Policy (SOP), which governs interactions between different origins to maintain security.
Key note here is that SOP is a client side defense. It is up to the browser (or other client) to enforce the SOP. This ensures that only trusted resources from intended domains are loaded and have access to sensitive content such as credentials.
Scenario | Scheme | Hostname | Port | Same Origin? |
---|---|---|---|---|
https://jhgfdsa.com/home vs https://jhgfdsa.com/about | https | jhgfdsa.com | 443 | ✅ Yes |
https://jhgfdsa.com vs http://jhgfdsa.com | https vs http | jhgfdsa.com | 443 vs 80 | ❌ No |
https://jhgfdsa.com vs https://assets.jhgfdsa.com | https | jhgfdsa.com vs assets.jhgfdsa.com | 443 | ❌ No |
https://jhgfdsa.com vs https://hydrohousehold.com | https | jhgfdsa.com vs hydrohousehold.com | 443 | ❌ No |
https://jhgfdsa.com:443 vs https://jhgfdsa.com:8443 | https | jhgfdsa.com | 443 vs 8443 | ❌ No |
Resources with identical scheme, hostname, and port are considered to be from the same origin. For instance, if you’ve two web pages with the URL “https://jhgfdsa.com/home
” and “https://jhgfdsa.com/about
“, they’re from the same origin as they share the same protocol (https), domain (jhgfdsa.com), and port (implicitly 443 for https).
Alternatively, variations in either protocol (https vs http), domain (jhgfdsa.com vs whatever.com), subdomain (assets.jhgfdsa.com vs jhgfdsa.com), or port (http/80 vs https/443) delineate different origins. Before implementing CORS, it is super important to understand SOP. Without the base understanding, there is a big risk of opening up end users to XSS and CORS related issues.
Same-Origin Policy (SOP) – What is the Point?
The Same-Origin Policy (SOP) is a browser security feature designed to stop malicious websites from executing cross origins attacks. The goal of SOP (or CORS) is to only allow a client side browser to interact and send sensitive information to trusted endpoints. Here’s an example:
Example: Without SOP
You’re logged into your bank at https://securebank.com
. Your session cookie keeps you authenticated, so you don’t need to log in repeatedly. Now imagine you visit a malicious site, https://badguy.com
. The attacker’s site https://badguy.com
runs JavaScript like this:
javascriptCopy codefetch('https://securebank.com/api/account-details', {
credentials: 'include',
}).then(response => response.json())
.then(data => console.log(data));
Here’s what’s happening:
- Your browser sends the request to
https://securebank.com
, including your session cookie. - Without SOP, the browser allows
https://
to see the response from your bank.badguy
.com - The attacker now has access to your account details.
Basically, without the SOP (or properly configured CORS) you are pwned. If you get lured to a malicious website and that website knows you bank at https://securebank.com
, they can get your details and potentially auto transfer the money from the account.
How SOP Fixes This
SOP ensures that only JavaScript running on https://securebank.com
can read responses from https://securebank.com
(because these are literally the same – same protocol, same site, same port). When the browser sees that https://
is trying to access a different origin (securebank.com), the browser blocks the response entirely.badguy
.com
In the example above (but now with SOP):
- The malicious request still reaches the bank.
- The bank might even process the request.
- But the browser blocks the response from being read by JS from
https://
.badguy
.com
The response (if one is received) is effectively “walled off” by the Same-Origin Policy, so the JavaScript from https://badguy.com
cannot use it. Even if the request is made and the results are returned to the browser, the bad actor (in this case JS on https://
) is stopped from being able to see the sensitive data.badguy
.com
Why Does This Matter?
SOP keeps control in the hands of the origin hosting the sensitive data (the people controlling the API). It doesn’t prevent every attack but it is a great defense stopping phishing type attacks. if you have Cross-Site Scripting (XSS), all bets are off – XSS allows the code to look like it is being ran from origin – in the case of stored XSS it actually is.
What Does SOP Really Lock Down?
The Same-Origin Policy (SOP) is the browser’s way of saying, “Stay in your lane.” It doesn’t block everything, but it ensures that certain interactions between origins are restricted. Here’s what SOP actually locks down:
- Cross-Origin Reads
fetch
andXMLHttpRequest
:- SOP prevents a script on
https://badguy.com
from reading the response of an API request tohttps://securebank.com
, even if the request is successfully sent. - Example:
fetch('https://securebank.com/api')
will fail unless the server explicitly allows it via CORS.
- SOP prevents a script on
- DOM Elements:
- SOP blocks scripts from accessing content inside an
<iframe>
that’s loaded from a different origin. - Example: A script on
https://example.com
cannot read the inner HTML of an iframe pointing tohttps://another.com
.
- SOP blocks scripts from accessing content inside an
- Storage Access
localStorage
andsessionStorage
:- Each origin gets its own isolated storage. A script on
https://badguy.com
cannot accesslocalStorage
orsessionStorage
data fromhttps://securebank.com
. - Example: If
https://securebank.com
saves a token inlocalStorage
, it’s completely invisible to other origins.
- Each origin gets its own isolated storage. A script on
document.cookie
:- Cookies are scoped to the origin. Scripts from a different subdomain or port cannot access cookies without explicit sharing (
Domain
attribute). - Example:
assets.jhgfdsa.com
cannot read cookies set forjhgfdsa.com
unless configured to share viaDomain=jhgfdsa.com
.
- Cookies are scoped to the origin. Scripts from a different subdomain or port cannot access cookies without explicit sharing (
- JavaScript and DOM Access
- Cross-Origin Scripting:
- SOP blocks JavaScript from interacting with the DOM of a different origin.
- Example: A script on
https://badguy.com
cannot call functions or manipulate elements in an iframe pointing tohttps://securebank.com
.
- Cross-Origin Scripting:
- Network Requests
- Cross-Origin Writes (Allowed):
- SOP allows cross-origin form submissions and some requests, but the response data is blocked.
- Example: A form on
https://badguy.com
can send data tohttps://securebank.com
, but it can’t read the response.
- Cross-Origin Embedding (Allowed):
- SOP permits embedding resources like images, scripts, and stylesheets across origins.
- Example: A script from
https://cdn.example.com
can execute onhttps://myapp.com
, but SOP doesn’t allow it to accesslocalStorage
.
- Cross-Origin Writes (Allowed):
Attacks Mitigated by SOP
Attack Category | How SOP Mitigates |
---|---|
Cross-Site Data Theft | Prevents unauthorized reading of data like cookies, localStorage, sessionStorage, and API responses. |
Cross-Origin Resource Manipulation | Blocks JavaScript from one origin from manipulating DOM elements of another origin. |
Credential Exploitation | Restricts unauthorized use of session cookies or authentication tokens across origins. |
Cross-Site Scripting (XSS) Data Exfiltration | Limits injected scripts from accessing cross-origin resources. |
Cross-Origin Request Forgery (CSRF) | Prevents responses from being read in unauthorized cross-origin requests (partial mitigation). |
Cross-Origin Storage Access | Prevents reading of localStorage, sessionStorage, or IndexedDB data from another origin. |
Loosening the Same-Origin Policy
Relaxing the Same-Origin Policy (SOP) is about controlled exceptions. This can be done via Cross-Origin Resource Sharing (CORS)—the mechanism that lets you define exactly which origins are allowed to access your resources.
Breakdown:
- CORS Headers: You decide who gets in. Allow a single origin (
Access-Control-Allow-Origin: https://hydrohousehold.com
), or a controlled list. Wildcard (*) is allowed, but this is reckless. - Preflight Requests: For more complex operations (e.g.,
PUT
,DELETE
, or custom headers), browsers send a preflightOPTIONS
request to confirm the origin is allowed. You’ve probably seen these in your Network tab.
Practical Security Tips
- Keep the CORS List Short: Work from the most restrictive as the starting point
- SOP Alone Isn’t Enough: SOP blocks cross-origin reads but doesn’t stop CSRF. Anti-CSRF tokens are still essential for sensitive data.
Relax SOP where you need to, but keep the controls tight. Loosening it doesn’t mean leaving the door wide open—it’s about balancing security with functionality. I will do a deeper dive into CORS in future posts.
Same-Origin Policy and Iframes
The Same-Origin Policy (SOP) directly influences how iframes can interact with the parent document and other resources. While iframes allow embedding content from different origins, SOP ensures that cross-origin interactions are restricted to maintain security. Basically, the parent document and the content within the iframe are only allowed to communicate if wired up specifically to do so.
Example: Cross-Origin Iframe Restrictions
Here’s the setup to demo this concept:
- Parent Page:
https://jhgfdsa.com/soptestpage.html
- Iframe Source:
https://hydrohousehold.com
The parent page embeds an iframe pointing to https://hydrohousehold.com
:
Attempt to Access Iframe Content
Now, a script in the parent page tries to access the iframe’s content:
const iframe = document.getElementById('hydroIframe');
try {
const iframeContent = iframe.contentDocument.body.innerHTML;
const hhhResultElement = document.getElementById('hhhResult');
hhhResultElement.textContent = iframeContent;
hhhResultElement.style.backgroundColor = 'lightgreen';
} catch (error) {
console.error('Error fetching data:', error);
const hhhResultElement = document.getElementById('hhhResult');
hhhResultElement.textContent = 'Failed to fetch data. Check the console for details.';
hhhResultElement.style.backgroundColor = 'lightpink';
}
The iframe.contentDocument.body.innerHTML code attempts to get ahold of the content within the iframe – and the iframe is delivered from a different origin.
What Happens?
Because https://hydrohousehold.com
is a different origin than https://jhgfdsa.com
, the browser blocks access to the iframe’s contentDocument
. The same-origin policy is in effect – what is interesting is that the browser silently blocks (at least within Chromium):
Here’s the page code:
<h2>Hydrohousehold Iframe</h2>
<div id="hhhResult">Loading...</div>
<iframe id="hydroIframe" src="https://hydrohousehold.com" width="600" height="400"></iframe>
And here is the result in the browser:
CORS is set to allow access from https://jhgfdsa.com
on the API so both of the outputs from same origin and cross origin work. However, the <div id=’hhhResult’> is not populated. In fact, the Loading… gets wiped out because the JS from above is not allowed access to the iframe content. As a result, the text in the hhhResult gets set to null or an empty string.
If you do need to communicate between the iframe and the main page, it is possible to do so with the postmessage mechanism, however, that is a topic for another post.
Conclusion
The Same-Origin Policy (SOP) is a key piece of browser security. It keeps things locked down, preventing your application from accidentally handing over sensitive data to untrusted origins. Understanding SOP is critical before configuring a CORS policy. Understanding SOP and CORS is also critical when working towards that next bug bounty.
Happy hacking!
FAQ
What is the Same-Origin Policy (SOP)?
The Same-Origin Policy (SOP) is a critical security mechanism implemented in web browsers that restricts how documents or scripts loaded from one origin can interact with resources from another origin. An origin is defined by the combination of the protocol, hostname, and port number of a web page. For example, two URLs with different domains or subdomains would be considered different origins, and hence the SOP would prevent them from interacting directly.
Why is the Same-Origin Policy important for web security?
The Same-Origin Policy is vital for maintaining browser security as it helps to prevent various types of attacks such as cross-site scripting (XSS) and cross-site request forgery (CSRF). By restricting how web applications can interact with different , the SOP mitigates the risk of malicious websites accessing sensitive data or performing actions on behalf of users without their consent. This security policy helps to create a safer world wide web by ensuring that scripts from one origin cannot access data or resources from another origin.
How does the Same-Origin Policy work?
The Same-Origin Policy allows scripts running on a web page to make requests to the same origin from which the script was loaded. If the script attempts to access a resource from a different origin, the browser will block the request unless the target server explicitly allows it by implementing Cross-Origin Resource Sharing (CORS) headers. This mechanism defines rules for how resources can be shared across origins, allowing for more flexible interactions while still maintaining security.
What are CORS headers and how do they relate to SOP?
CORS headers are HTTP headers used to allow or restrict resources being requested from different origins. They are integral to enabling Cross-Origin Resource Sharing, which provides a way to relax the Same-Origin Policy by specifying which origins are permitted to
Why does the browser treat subdomains as different origins under the Same-Origin Policy (SOP)?
Subdomains are treated as different origins because SOP enforces strict isolation based on the hostname, along with the scheme and port. This prevents potentially untrusted subdomains from accessing sensitive resources on the parent domain or other subdomains. For example, https://assets.jhgfdsa.com
cannot access cookies or storage scoped to https://jhgfdsa.com
unless explicitly configured to share them. This isolation protects against attacks like cross-site scripting (XSS) that could exploit weaker subdomain security.