The “Same Origin” (same site) policy limits access of windows and frames to each other.
The idea is that if a user has two pages open: one from john-smith.com, and another one is gmail.com, then they wouldn’t want a script from john-smith.com to read our mail from gmail.com. So, the purpose of the “Same Origin” policy is to protect users from information theft.
Same Origin [#same-origin]
Two URLs are said to have the “same origin” if they have the same protocol, domain and port.
These URLs all share the same origin:
- http://site.com
- http://site.com/
- http://site.com/my/page.html
These ones do not:
- http://www.site.com (another domain: www. matters)
- http://site.org (another domain: .org matters)
- https://site.com (another protocol: https)
- http://site.com:8080 (another port: 8080)
The “Same Origin” policy states that:
- if we have a reference to another window, e.g. a popup created by window.open or a window inside
In action: iframe
An
Windows on subdomains: document.domain
By definition, two URLs with different domains have different origins.
But if windows share the same second-level domain, for instance john.site.com, peter.site.com and site.com (so that their common second-level domain is site.com), we can make the browser ignore that difference, so that they can be treated as coming from the “same origin” for the purposes of cross-window communication.
To make it work, each such window should run the code:
That’s all. Now they can interact without limitations. Again, that’s only possible for pages with the same second-level domain.
Iframe: wrong document pitfall
When an iframe comes from the same origin, and we may access its document, there’s a pitfall. It’s not related to cross-origin things, but important to know.
Upon its creation an iframe immediately has a document. But that document is different from the one that loads into it!
So if we do something with the document immediately, that will probably be lost.
Here, look:
We shouldn’t work with the document of a not-yet-loaded iframe, because that’s the wrong document. If we set any event handlers on it, they will be ignored.
How to detect the moment when the document is there?
The right document is definitely at place when iframe.onload triggers. But it only triggers when the whole iframe with all resources is loaded.
We can try to catch the moment earlier using checks in setInterval:
Collection: window.frames
An alternative way to get a window object for – is to get it from the named collection window.frames:
- By number: window.frames[0] – the window object for the first frame in the document.
- By name: window.frames.iframeName – the window object for the frame with name=“iframeName”.
For instance:
An iframe may have other iframes inside. The corresponding window objects form a hierarchy.
Navigation links are:
- window.frames – the collection of “children” windows (for nested frames).
- window.parent – the reference to the “parent” (outer) window.
- window.top – the reference to the topmost parent window.
For instance:
We can use the top property to check if the current document is open inside a frame or not:
The “sandbox” iframe attribute
allow-same-origin
By default “sandbox” forces the “different origin” policy for the iframe. In other words, it makes the browser to treat the iframe as coming from another origin, even if its src points to the same site. With all implied restrictions for scripts. This option removes that feature.
allow-top-navigation
Allows the iframe to change parent.location.
allow-forms
Allows to submit forms from iframe.
allow-scripts
Allows to run scripts from the iframe.
allow-popups
Allows to window.open popups from the iframe
See the manual for more.
The example below demonstrates a sandboxed iframe with the default set of restrictions: . It has some JavaScript and a form.
Please note that nothing works. So the default set is really harsh:
[codetabs src=“sandbox” height=140]
Cross-window messaging
The postMessage interface allows windows to talk to each other no matter which origin they are from.
So, it’s a way around the “Same Origin” policy. It allows a window from john-smith.com to talk to gmail.com and exchange information, but only if they both agree and call corresponding JavaScript functions. That makes it safe for users.
The interface has two parts.
postMessage
The window that wants to send a message calls postMessage method of the receiving window. In other words, if we want to send the message to win, we should call win.postMessage(data, targetOrigin).
Arguments:
data
The data to send. Can be any object, the data is cloned using the “structured serialization algorithm”. IE supports only strings, so we should JSON.stringify complex objects to support that browser.
targetOrigin
Specifies the origin for the target window, so that only a window from the given origin will get the message.
The targetOrigin is a safety measure. Remember, if the target window comes from another origin, we can’t read its location in the sender window. So we can’t be sure which site is open in the intended window right now: the user could navigate away, and the sender window has no idea about it.
Specifying targetOrigin ensures that the window only receives the data if it’s still at the right site. Important when the data is sensitive.
For instance, here win will only receive the message if it has a document from the origin http://example.com:
If we don’t want that check, we can set targetOrigin to *.
onmessage
To receive a message, the target window should have a handler on the message event. It triggers when postMessage is called (and targetOrigin check is successful).
The event object has special properties:
data
The reference to the sender window. We can immediately source.postMessage(…) back if we want.
To assign that handler, we should use addEventListener, a short syntax window.onmessage does not work.
Here’s an example:
The full example:
[codetabs src=“postmessage” height=120]
Summary
To call methods and access the content of another window, we should first have a reference to it.
For popups we have these references:
From the opener window: window.open – opens a new window and returns a reference to it,
From the popup: window.opener – is a reference to the opener window from a popup.
For iframes, we can access parent/children windows using:
window.frames – a collection of nested window objects,
window.parent, window.top are the references to parent and top windows,
iframe.contentWindow is the window inside an tag.
If windows share the same origin (host, port, protocol), then windows can do whatever they want with each other.
Otherwise, only possible actions are:
Change the location of another window (write-only access).
Post a message to it.
Exceptions are:
Windows that share the same second-level domain: a.site.com and b.site.com. Then setting document.domain=‘site.com’ in both of them puts them into the “same origin” state.
If an iframe has a sandbox attribute, it is forcefully put into the “different origin” state, unless the allow-same-origin is specified in the attribute value. That can be used to run untrusted code in iframes from the same site.
The postMessage interface allows two windows with any origins to talk:
The sender calls targetWin.postMessage(data, targetOrigin).
If targetOrigin is not ‘*’, then the browser checks if window targetWin has the origin targetOrigin.
If it is so, then targetWin triggers the message event with special properties:
data – the data, any object in everywhere except IE that supports only strings.
We should use addEventListener to set the handler for this event inside the target window.
<iframe src="https://example.com" id="iframe"></iframe>
<script>
iframe.onload = function() {
// we can get the reference to the inner window
*!*
let iframeWindow = iframe.contentWindow; // OK
*/!*
try {
// ...but not to the document inside it
*!*
let doc = iframe.contentDocument; // ERROR
*/!*
} catch(e) {
alert(e); // Security Error (another origin)
}
// also we can't READ the URL of the page in iframe
try {
// Can't read URL from the Location object
*!*
let href = iframe.contentWindow.location.href; // ERROR
*/!*
} catch(e) {
alert(e); // Security Error
}
// ...we can WRITE into location (and thus load something else into the iframe)!
*!*
iframe.contentWindow.location = '/'; // OK
*/!*
iframe.onload = null; // clear the handler, not to run it after the location change
};
</script>
Example:
Follow the lesson from Microsoft Web-Dev-For-Beginners course