Same Origin Policy
The browser-side defence we know little about....
The Same Origin policy is a security mechanism which defines how a document, resource or script loaded from one origin interacts with another origin. It is a web browser security mechanism that prevents websites from "attacking" each other.
What is an origin?
The origin of a URI is defined as the combination of its protocol/schema, the hostname and the port. An example of this is shown below:
Two URLs have the same origin if the protocol, port (if specified), and host are the same for both.
Why is SOP needed?
Let's take an example of two websites, www.bank.com
a legit site, and www.totallysafenothingmaliciousbank.com
an evil site. If the user is logged in www.bank.com
and simultaneously opens www.totallysafenothingmaliciousbank.com
in a new tab, the javascript code in the evil site can manipulate the legit site, IF THE SAME ORIGIN POLICY IS NOT PRESENT.
SOP actually helps in the separation of concerns. It helps isolate potentially malicious documents, reducing possible attack vectors.
SOP Implementation
The same-origin policy prevents the above scenario from happening by blocking read access to resources loaded from a different origin.
But we do load images, iframes, videos, fonts, scripts, etc from CDNs or other different origins, right? How does it work in that case?
Answer: Browsers allow a few tags to embed resources from a different origin. SOP is decided on multiple factors as shown in the table below:
Resource | Allowed | Disallowed/Blocked |
---|---|---|
iframe | Cross-origin embedding is usually permitted (depending on the X-Frame-Options directive). | Cross-origin reading (such as using JavaScript to access a document in an iframe) |
CSS | Cross-origin CSS can be embedded using a | Read the style contents, like using: |
forms | Cross-origin URLs can be used as the | Credentialed cross-origin JSON HTML form is not allowed. The browser defaults to The browser will fallback to application/x-www-form-urlencoded. |
images | Embedding cross-origin images is permitted. | Reading cross-origin image data (such as retrieving binary data from a cross-origin image, modifying pixels, etc, using JavaScript) is blocked. |
scripts | Cross-origin scripts can be embedded. | however, access to certain APIs (such as cross-origin fetch requests) might be blocked. |
Deeper dive into what works and what doesn't
While the above table gives a gist about what's allowed and what's not. Let's take a closer look at them to understand the security concerns further.
For this, we are going to take the help of two websites, say www.website-a.com
and www.website-b.com
Resource Embedding
Loading styles
Stylesheets can be loaded using the <link>
tag. But a cross-site stylesheet content access and modification using javascript is a bit restricted.
Embedding images
Simple image loading with the <image>
tag is allowed but the modification of image pixels etc isn't allowed.
Similarly, we can create a canvas for an image from literally any website, but, reading pixels from that canvas isn't allowed.
Javascript Controls
Embedding scripts
JavaScript with <script src="…"></script>
can be embedded without any issue, however, access to certain APIs (such as cross-origin fetch requests) might be blocked. Error details for syntax errors are only available for same-origin scripts.
In general, embedding any resource (image, style, script, etc.) is allowed cross-origin, but JavaScript cannot directly access the resource.
What's allowed?
Reading scripts from different sites is allowed
To explain this, let's take a situation, where we have the HTML page on www.website-a.com
and the javascript code is being served from www.website-b.com
. Website-a embeds the scripts using a script tag (PS: this is how CDNs work).
Now this is allowed by same-origin policy as the "origin" of the script is the page it is executed in, not where it comes from (Takes the quote "It's not where you are from, but, what you do that defines you" to another level :P).
Usage of data and functions provided by the embedded script
Let's say the script from the website-b
has some variable x. We can actually console.log()
it and see its value, even when it's not being served from website-a
. This is how JSONP works but it's not used anymore.
This has a direct security impact of making the website prone to XSSI (Cross-Site Script Inclusion) attacks if the website serves dynamic JavaScript files with authenticated user data in them.
What's not allowed?
Reading the script source - There is no way to do it anyway though
Window Access
Generally, a website has complete control over the window that it's running on, but, there are many ways in which one website can control/handle another window.
Some of the methods include:
1. Using window.open
.
2. Creating an iframe
.
3. Using window.opener
if the website is framed by another.
4. Using postMessage()
method.
Example snippet
Let's take an example snippet to understand the concepts stated below in this blog.
In the above script, we are creating a cross-origin frame within http://www.website-a.com
. Now we will see what operations are allowed on the contentWindow
we created.
The contentWindow
property returns the Window object of an HTMLIFrameElement. You can use this Window
object to access the iframe's document and its internal DOM. This attribute is read-only, but its properties can be manipulated like the global Window
object.
What's allowed?
Reading the number of frames in a cross-origin window
For a window, we can know the number of frames it's embedding. So the following snippet is valid.
Replacing/Write the source URI for the cross-origin frame
It is absolutely possible to replace the source URI of a cross-origin iframe. The following snippet is actually valid.
This has a direct security impact as the websites that you "frame" on your website can get a window handle to it via the window.opener property. This means that if you load a malicious website in an iframe on your website, the frame can change the URI of your site into, e.g., a phishing page.
This can be stopped by using the sandbox
property. The sandbox
attribute enables an extra set of restrictions for the content in the iframe.
When the sandbox
attribute is present, and it will:
treat the content as being from a unique origin
block form submission
block script execution
disable APIs
prevent links from targeting other browsing contexts
prevent content from using plugins (through
<embed>
,<object>
,<applet>
, or other)prevent the content to navigate its top-level browsing context
block automatically triggered features (such as automatically playing a video or automatically focusing a form control)
The value of the sandbox
attribute can either be empty (then all restrictions are applied) or a space-separated list of pre-defined values that will REMOVE the particular restrictions.
Sending messages to the window via postMessage()
postMessage()
The postMessage method allows cross-origin windows to communicate with each other. So let's say the iframe that we are loading (from website-b into website-a) has some way to parse the message sent to it, as shown in the snippet below:
Now when the script in website-a
sends a message in the way shown below, the message simply gets reflected in the iframe, but, it's actually parsed and displayed by website-b
behind the scenes.
What's not allowed?
Reading/Writing cross-origin iframe content
Cross-origin reading (such as using JavaScript to access a document in a "cross-origin" iframe) and writing (using JavaScript to modify a document in a "cross-origin" iframe) aren't allowed.
So the following snippets:
Throws the error:
Reading the source URI of a cross-origin iframe
Yes! READING THE SOURCE URI ISN'T ALLOWED BUT WRITING/MODIFYING IT IS ALLOWED
.
Throws the error:
Reading localStorage or sessionStorage via ContentWindow object of a cross-origin iframe
Window.localStorage
returns a reference to the local storage object used to store data that may only be accessed by the origin that created it. This is a read-only attribute.
Window.sessionStorage
returns a reference to the session storage object used to store data that may only be accessed by the origin that created it.
A cross-origin iframe window isn't allowed "read" access to the local storage and session storage objects. So the following snippets (similar ones too) are blocked by SOP:
Last updated