Error: Permission denied to access property "document" - javascript

I have a HTML Document which contains an iframe. Whenever I try to access or modify this iframe with JS I get Error: Permission denied to access property "document".
I am using frame.contentWindow.document.body.innerHTML or frame.contentWindow.document.body.onload or similar such attributes to access or modify the iframe. (In the given code the iframe is referred to as frame.)
For the web-app I am developing, access to these attributes are necessary and I can't do without these (or similar alternatives).

Accessing and then modifying webpages in iframes of other websites is known as Cross-site scripting or XSS and it is a technique used by malicious hackers to prey on unsuspecting victims.
A policy by the name of "Same-Origin Policy" is implemented by browser makers to prevent such behaviour and arbitrary execution of JS code.
This error can be prevented by hosting the parent document and the document in the iframe in the same domain and subdomain, and making sure that the documents are loaded using the same protocol.
Examples of Incompatible Pages:
http://www.example.org & http://www.example2.com
http://abc.example.org & http://xyz.example.com
http://www.example.org & https://www.example.com
Cross-Origin Resource Sharing is a solution to this problem.
For Example:
If http://www.example.com would like to share http://www.example.com/hello with http://www.example.org, a header can be sent with the document which looks like the following:
Access-Control-Allow-Origin: http://www.example.org
To send it with HTML just put it in a <META HTTP-EQUIV="..."> tag, like this:
<head>
...
<META HTTP-EQUIV="Access-Control-Allow-Origin" CONTENT="http://www.example.org">
...
</head>

You can still bypass this issue with the help of YQL even though you don't have access to the header part of the receiving window. With the Postmessage method also you need to edit the recipient window script. But using this method you can load any iframe without touching their scripts. Check this out!
<html>
<iframe src="https://google.com/" width="500" height="300"></iframe>
<script>
var iframe = document.getElementsByTagName('iframe')[0];
var url = iframe.src;
var getData = function (data) {
if (data && data.query && data.query.results && data.query.results.resources && data.query.results.resources.content && data.query.results.resources.status == 200) loadHTML(data.query.results.resources.content);
else if (data && data.error && data.error.description) loadHTML(data.error.description);
else loadHTML('Error: Cannot load ' + url);
};
var loadURL = function (src) {
url = src;
var script = document.createElement('script');
script.src = 'https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20data.headers%20where%20url%3D%22' + encodeURIComponent(url) + '%22&format=json&diagnostics=true&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&callback=getData';
document.body.appendChild(script);
};
var loadHTML = function (html) {
iframe.src = 'about:blank';
iframe.contentWindow.document.open();
iframe.contentWindow.document.write(html.replace(/<head>/i, '<head><base href="' + url + '"><scr' + 'ipt>document.addEventListener("click", function(e) { if(e.target && e.target.nodeName == "A") { e.preventDefault(); parent.loadURL(e.target.href); } });</scr' + 'ipt>'));
iframe.contentWindow.document.close();
}
loadURL(iframe.src);
</script>
</html>

You can use postMessage
Window 1 - receiving
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
var origin = event.origin || event.originalEvent.origin;
// For Chrome, the origin property is in the event.originalEvent object.
if (origin !== "http://example.org:8080")
return;
// ...
}
Window - 2 Transmitting
var popup = window.open(...popup details...);
popup.postMessage(
"The user is 'bob' and the password is 'secret'",
"https://secure.example.net"
);
You have to create another pair to intercommunicate.

Related

Cannot check iframe window location from a website with different origin

I have a script that create an iframe like this, and I use the iframe to check authentication then redirect the main window :
var target "https://my.website.it"
var iframe = document.createElement("iframe");
iframe.id = "my-frame";
iframe.src = "/my-url/that?redirect=true&target=" + target
iframe.onload = function() {
iframeFn();
};
into the iframeFn() function I want to check the location of the iframe itself to perform some controls before redirect:
function iframeFn() {
var myFrame = document.getElementById("my-frame");
var iframeWindow = myFrame.contentWindow;
if (iframeWindow.location.search.search(/fail/) >= 0) {
window.location = '/'
}
I put this script in a cdn and I use this script in a website with the same origin url of the redirect target (https://my.website.it), and it works. But if I try to use this script in a website with different origin (https://different.website.it) I got this error:
Uncaught DOMException: Blocked a frame with origin "https://different.website.it" from accessing a cross-origin frame.
at reloadInIFrame (https://static.website.it/my-script.js:34:29)
at HTMLIFrameElement.iframe.onload (https://static.website.it/.js:82:5)
at this line
if (iframeWindow.location.search.search(/fail/) >= 0) {
I've read this: SecurityError: Blocked a frame with origin from accessing a cross-origin frame but I can't figure out how to use window.postMessage in my case.
NB: the second level domain is the same in both cases (website.it)
Thanks for your help!

Print a form from another link on clicking a button

I'm trying to figure out how to print an image from another web page link on clicking a button.
I know window.print() but how could I specify the other link I want to print the image from?
Same domain
If the page you wish to print is from the same domain as the iframe's parent then MDN has a good example of how to do this.
You should create a hidden iframe, load your page in it, print the iframe contents and then remove the iframe.
JavaScript:
function printURL( url ) {
var frame = document.createElement( "iframe" );
frame.onload = printFrame;
frame.style.display = 'none';
frame.src = url;
document.body.appendChild(frame);
return false;
}
function printFrame() {
this.contentWindow.__container__ = this;
this.contentWindow.onbeforeunload = closeFrame;
this.contentWindow.onafterprint = closeFrame;
this.contentWindow.focus(); // Required for IE
this.contentWindow.print();
}
function closeFrame () {
document.body.removeChild(this.__container__);
}
HTML:
<button onclick="printURL('page.html');">Print external page!</button>
Cross domain
If the page you wish to print is from another domain then your browser will throw a Same-Origin Policy error. This is a security feature that forbids scripts accessing some data from different domains.
To print cross domain content you will need to scrape the page's source and load it into the iframe. The browser will then believe that the iframe's content comes from your domain and won't hiccough when you try to print.
However, if you try to do this in the frontend, this just pushes the problem back one step further, as the same-origin policy also won't let you scrape content from another domain in this way. But the same-origin policy for data scraping is the equivalent of tying a bull up with cotton thread - it doesn't really hold you back - so this hurdle is easily circumvented. You can either write your own backend script (in PHP or your choice of language) that will scrape the content and deliver it to your page, or you can use any one of a number of web services that already do this. https://multiverso.me/AllOrigins/ is as good as any, it doesn't require backend programming, and it's free so I'll use that in this example.
Using Jquery, the modified printURL function from above would be:
function printURL( url ) {
var jsonUrl = 'http://allorigins.me/get?url=' + encodeURIComponent(url) + '&callback=?';
// the url / php function that allows content to be scraped from different origins.
$.getJSON( jsonUrl, function( data ) {
// get the scraped content in data.content
var frame = document.createElement( "iframe" ),
iframedoc = frame.contentDocument || frame.contentWindow.document;
frame.onload = printFrame;
frame.style.display = 'none';
iframedoc.body.html( data.contents );
document.body.appendChild(frame);
}
return false;
}
The other functions from above would remain the same.
Note that if the page you're printing is built using AJAX calls or is significantly styled with scripting then the iframe may print something that looks quite unlike what you were expecting.

Cross Domain communication from Child (iframe) to Parent not working

I have a component within AEM (Adobe Experience Manager - a cms) on a page and I want to include this page onto another page (from a different domain) using an iframe. So in the code for the component I am using window.postMessage() and I'm trying to listen to that event in the parent. I have tried communicating the other way, parent to iframe and it worked fine, but I need to communicate the other way. So the component is a search component and when you click on a search result I want to redirect but from the parent window so I'm trying to send the URL to redirect to and then handle the redirection within the parent's JS code.
The code looks like:
(From the parent - html)
<iframe
width="1080"
height="700"
id="theFrame"
src="http://localhost:4502/content/zebra1/global/en_us/hey.html#q=print"
frameborder="0">
</iframe>
(From the parent - js)
function receiveMessage(e)
{
var key = e.message ? "message" : "data";
var data = e[key];
var redirect = JSON.parse(data);
redirectUrl = (redirect.origin ? redirect.origin : '') + (redirect.url ?
redirect.url : '');
if (redirectUrl) {
window.location.href = redirectUrl;
}
}
window.addEventListener("message", receiveMessage, false);
(From the iframe/child - js)
goToSearchResults : function( event ){
var windowOrigin = location.origin;
if( arguments[0].length == 3){
var redirect = {
origin: windowOrigin,
url: arguments[0][1].url || ''
};
if(!$('#supportSearchWrap').data('iframe')) {
location.replace(redirect.url);
} else {
window.postMessage(JSON.stringify(redirect), windowOrigin);
}
}
logger.log( redirect.origin + redirect.url , this.model );
}
It's not working for me. Does anyone see what I'm doing wrong or a better way to do this?
window.postMessage - The window refers to the instance of the window object to which you're posting your message. In your case, it should be the parent of the iframe window.
You can get that reference inside the iframe using window.parent or simply parent.
Also, the targetOrigin property should match the targeted window properties. From MDN docs, it is as below.
targetOrigin
Specifies what the origin of otherWindow must be for the event to be dispatched, either as the literal string "*" (indicating no preference) or as a URI. If at the time the event is scheduled to be dispatched the scheme, hostname, or port of otherWindow's document does not match that provided in targetOrigin, the event will not be dispatched; only if all three match will the event be dispatched.

How to edit my code to save the same value of the domain.com to www.domain.com?

What I have below is a script that passes a variable from the crossvalue.html to an iframe crossvaluePOST.html and creates a localstorage to the (without www) domain.com
My question is how can I create the same localstorage value for the www.domain.com when domain.com/crossvalue.html (without www) runs?
this is crossvalue.html
<iframe id="da-iframe" src="crossvaluePOST.html"></iframe>
<script>
window.onload = function () {
var iframeWin = document.getElementById("da-iframe").contentWindow;
myMessage= Math.floor(Math.random()*801);
iframeWin.postMessage(myMessage, "http://domain.com");
return false;
};
</script>
and this is the crossvaluePOST.html
<script>
function displayMessage (evt) {
var message;
if (evt.origin !== "http://domain.com") {
message = "You are not worthy";
}
else {
localStorage['abc'] = evt.data;
}
}
if (window.addEventListener) {
// For standards-compliant web browsers
window.addEventListener("message", displayMessage, false);
}
else {
window.attachEvent("onmessage", displayMessage);
}
</script>
According to "use localStorage across subdomains" (which you linked yourself), the way to do this is to load a page from the other subdomain into your page, and communicate with that page to enter the values. This is necessary because of same-origin policy: a script on a webpage is only allowed to access localstorage from the domain the site was loaded from.
In your case that means, that domain.com/crossvaluePOST.html cannot access the localstorage for www.domain.com, but only domain.com. TO change this, you need to load www.domain.com/crossvaluePOST.html in domain.com/crossvalue.html (and domain.com/crossvaluePOST.html in www.domain.com/crossvalue.html if you need both directions).
Just change your crossvalue.html to
<iframe id="da-iframe" src="//www.domain.com/crossvaluePOST.html"></iframe>
<script>
// ...
</script>
to write the www.domain.com localstorage when the page is accessed using domain.com (if you need both directions, you obviously have to change the URL based on where the page is loaded from)
Disclaimer: this is just taken from the post you linked, I never tried this.

Getting URL of the top frame

Inside a facebook application I need to check what is the top frame (the main window) URL, and show content accordingly.
I tried using the following:
if (top.location.toString().toLowerCase().indexOf("facebook.com") <0) { ... }
Which works well if the page is not inside an iframe, but when the page is loaded within an iframe (as it does when used as facebook application) the code generates
"Uncaught TypeError: Property
'toString' of object # is not a
function".
Is there any way I can fix this code (with cross-browser compatibility - maybe with jQuery)?
Thanks!
Joel
It is true that cross origin concerns will prevent you from accessing this top window location. However, if you just want the parent window location of the iframe you can get at it via the document.referrer string.
Within your iframe you'd grab the url:
var parentURL = document.referrer
https://developer.mozilla.org/en-US/docs/Web/API/document.referrer
I've used this successfully in my own iframe apps. Also, be aware that if you navigate within your iframe the referrer will change.
Nicholas Zakas has a write-up on his blog:
http://www.nczonline.net/blog/2013/04/16/getting-the-url-of-an-iframes-parent/
The problem you are having that you are not allowed to access top.location across different document domains.
This is a security feature built in to browsers.
Read up on XSS and why the security precautions are in place :)
You can also learn a great deal by reading about the same origin policy
With Martin Jespersen adviced fix, I could check address in iFrame and standart top address:
//this is fix for IE
if (!window.location.origin) {
window.location.origin = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: '');
}
//and here we will get object of address
var urls = (window.location != window.parent.location) ? document.referrer: document.location;
//Martins adviced fix for checking if You are not in iFrame
if (window.top === window) {
urls = urls.origin;
}
//and now indexOf works in both ways - for iFrame and standart top address
if (urls.indexOf("facebook.com") != -1 ) {
//do stuff
}
This could work:
if (self!=top && document.referrer.toLowerCase().indexOf("facebook.com") <0) { ... }
...as long as you don't navigate inside the frame.
But it's not really a good solution ^^
If you need as much information as possible about the top page location:
function getTopLinkInfo() {
var topLinkInfo = {};
try {
// Only for same origins
topLinkInfo.topHref = top.location.href;
}
// Security exception: different origins
catch (error) {
try {
var ancestorOrigins = window.location.ancestorOrigins;
// Firefox doesn't support window.location.ancestorOrigins
if (ancestorOrigins) {
topLinkInfo.parentsDomains = [];
for (var i = 0; i < ancestorOrigins.length; i++) {
topLinkInfo.parentsDomains.unshift(ancestorOrigins[i]);
}
}
// Sometimes referrer refers to the parent URL (but not always,
// e.g. after iframe redirects).
var bottomW = window;
var topW = window.parent;
topLinkInfo.parentsReferrers = [];
// In case of empty referrers
topLinkInfo.parentsHrefs = [];
while (topW !== bottomW) {
topLinkInfo.parentsReferrers.unshift(bottomW.document.referrer);
topLinkInfo.parentsHrefs.unshift(bottomW.location.href);
bottomW = bottomW.parent;
topW = topW.parent;
}
} catch (error) {/* Permission denied to access a cross-origin frame */}
}
return topLinkInfo;
}
var result = getTopLinkInfo();
console.table(result);
console.info(result);

Categories

Resources