JS cross domain [duplicate] - javascript
I am loading an <iframe> in my HTML page and trying to access the elements within it using JavaScript, but when I try to execute my code, I get the following error:
SecurityError: Blocked a frame with origin "http://www.example.com" from accessing a cross-origin frame.
How can I access the elements in the frame?
I am using this code for testing, but in vain:
$(document).ready(function() {
var iframeWindow = document.getElementById("my-iframe-id").contentWindow;
iframeWindow.addEventListener("load", function() {
var doc = iframe.contentDocument || iframe.contentWindow.document;
var target = doc.getElementById("my-target-id");
target.innerHTML = "Found it!";
});
});
Same-origin policy
You can't access an <iframe> with different origin using JavaScript, it would be a huge security flaw if you could do it. For the same-origin policy browsers block scripts trying to access a frame with a different origin.
Origin is considered different if at least one of the following parts of the address isn't maintained:
protocol://hostname:port/...
Protocol, hostname and port must be the same of your domain if you want to access a frame.
NOTE: Internet Explorer is known to not strictly follow this rule, see here for details.
Examples
Here's what would happen trying to access the following URLs from http://www.example.com/home/index.html
URL RESULT
http://www.example.com/home/other.html -> Success
http://www.example.com/dir/inner/another.php -> Success
http://www.example.com:80 -> Success (default port for HTTP)
http://www.example.com:2251 -> Failure: different port
http://data.example.com/dir/other.html -> Failure: different hostname
https://www.example.com/home/index.html:80 -> Failure: different protocol
ftp://www.example.com:21 -> Failure: different protocol & port
https://google.com/search?q=james+bond -> Failure: different protocol, port & hostname
Workaround
Even though same-origin policy blocks scripts from accessing the content of sites with a different origin, if you own both the pages, you can work around this problem using window.postMessage and its relative message event to send messages between the two pages, like this:
In your main page:
const frame = document.getElementById('your-frame-id');
frame.contentWindow.postMessage(/*any variable or object here*/, 'https://your-second-site.example');
The second argument to postMessage() can be '*' to indicate no preference about the origin of the destination. A target origin should always be provided when possible, to avoid disclosing the data you send to any other site.
In your <iframe> (contained in the main page):
window.addEventListener('message', event => {
// IMPORTANT: check the origin of the data!
if (event.origin === 'https://your-first-site.example') {
// The data was sent from your site.
// Data sent with postMessage is stored in event.data:
console.log(event.data);
} else {
// The data was NOT sent from your site!
// Be careful! Do not use it. This else branch is
// here just for clarity, you usually shouldn't need it.
return;
}
});
This method can be applied in both directions, creating a listener in the main page too, and receiving responses from the frame. The same logic can also be implemented in pop-ups and basically any new window generated by the main page (e.g. using window.open()) as well, without any difference.
Disabling same-origin policy in your browser
There already are some good answers about this topic (I just found them googling), so, for the browsers where this is possible, I'll link the relative answer. However, please remember that disabling the same-origin policy will only affect your browser. Also, running a browser with same-origin security settings disabled grants any website access to cross-origin resources, so it's very unsafe and should NEVER be done if you do not know exactly what you are doing (e.g. development purposes).
Google Chrome
Mozilla Firefox
Safari
Opera: same as Chrome
Microsoft Edge: same as Chrome
Brave: same as Chrome
Microsoft Edge (old non-Chromium version): not possible
Microsoft Internet Explorer
Complementing Marco Bonelli's answer: the best current way of interacting between frames/iframes is using window.postMessage, supported by all browsers
Check the domain's web server for http://www.example.com configuration for X-Frame-Options
It is a security feature designed to prevent clickJacking attacks,
How Does clickJacking work?
The evil page looks exactly like the victim page.
Then it tricked users to enter their username and password.
Technically the evil has an iframe with the source to the victim page.
<html>
<iframe src='victim-domain.example'/>
<input id="username" type="text" style="display: none;"/>
<input id="password" type="text" style="display: none;"/>
<script>
//some JS code that click jacking the user username and input from inside the iframe...
<script/>
<html>
How the security feature work
If you want to prevent web server request to be rendered within an iframe add the x-frame-options
X-Frame-Options DENY
The options are:
SAMEORIGIN: allow only to my own domain render my HTML inside an iframe.
DENY: do not allow my HTML to be rendered inside any iframe
ALLOW-FROM https://example.com/: allow specific domain to render my HTML inside an iframe
This is IIS config example:
<httpProtocol>
<customHeaders>
<add name="X-Frame-Options" value="SAMEORIGIN" />
</customHeaders>
</httpProtocol>
The solution to the question
If the web server activated the security feature it may cause a client-side SecurityError as it should.
For me i wanted to implement a 2-way handshake, meaning:
- the parent window will load faster then the iframe
- the iframe should talk to the parent window as soon as its ready
- the parent is ready to receive the iframe message and replay
this code is used to set white label in the iframe using [CSS custom property]
code:
iframe
$(function() {
window.onload = function() {
// create listener
function receiveMessage(e) {
document.documentElement.style.setProperty('--header_bg', e.data.wl.header_bg);
document.documentElement.style.setProperty('--header_text', e.data.wl.header_text);
document.documentElement.style.setProperty('--button_bg', e.data.wl.button_bg);
//alert(e.data.data.header_bg);
}
window.addEventListener('message', receiveMessage);
// call parent
parent.postMessage("GetWhiteLabel","*");
}
});
parent
$(function() {
// create listener
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";
eventer(messageEvent, function (e) {
// replay to child (iframe)
document.getElementById('wrapper-iframe').contentWindow.postMessage(
{
event_id: 'white_label_message',
wl: {
header_bg: $('#Header').css('background-color'),
header_text: $('#Header .HoverMenu a').css('color'),
button_bg: $('#Header .HoverMenu a').css('background-color')
}
},
'*'
);
}, false);
});
naturally you can limit the origins and the text, this is easy-to-work-with code
i found this examlpe to be helpful:
[Cross-Domain Messaging With postMessage]
There is a workaround, actually, for specific scenarios.
If you have two processes running on the same domain but different ports, the two Windows can interact without limitations. (i.e. localhost:3000 & localhost:2000). To make this work, each window needs to change their domain to the shared origin:
document.domain = 'localhost'
This also works in the scenario that you are working with different subdomains on the same second-level domain, i.e. you are on john.site.example trying to access peter.site.example or just site.example
document.domain = 'site.example'
By explicitily setting document.domain; the browser will ignore the hostname difference and the Windows can be treated as coming from the 'same-origin'. Now, in a parent window, you can reach into the iframe: frame.contentWindow.document.body.classList.add('happyDev')
If you have control over the content of the iframe - that is, if it is merely loaded in a cross-origin setup such as on Amazon Mechanical Turk - you can circumvent this problem with the <body onload='my_func(my_arg)'> attribute for the inner html.
For example, for the inner html, use the this html parameter (yes - this is defined and it refers to the parent window of the inner body element):
<body onload='changeForm(this)'>
In the inner html :
function changeForm(window) {
console.log('inner window loaded: do whatever you want with the inner html');
window.document.getElementById('mturk_form').style.display = 'none';
</script>
I experienced this error when trying to embed an iframe and then opening the site with Brave. The error went away when I changed to "Shields Down" for the site in question. Obviously, this is not a full solution, since anyone else visiting the site with Brave will run into the same issue. To actually resolve it I would need to do one of the other things listed on this page. But at least I now know where the problem lies.
I would like to add Java Spring specific configuration that can effect on this.
In Web site or Gateway application there is a contentSecurityPolicy setting
in Spring you can find implementation of WebSecurityConfigurerAdapter sub class
contentSecurityPolicy("
script-src 'self' [URLDomain]/scripts ;
style-src 'self' [URLDomain]/styles;
frame-src 'self' [URLDomain]/frameUrl...
...
.referrerPolicy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)
Browser will be blocked if you have not define safe external contenet here.
Open the start menu
Type windows+R or open "Run
Execute the following command.
chrome.exe --user-data-dir="C://Chrome dev session" --disable-web-security
Related
Detect URL of a page that included my iframe [duplicate]
This question already has answers here: SecurityError: Blocked a frame with origin from accessing a cross-origin frame (9 answers) Closed 1 year ago. My website provides functionality for embedding a video from my website via an iframe (similar to Youtube or any other video portals). However, is there a way to exactly detect, which website included mine as an iframe. Tried within my iframe embed, trying to detect parent's URL if (window !== window.top) { console.log(window.top.location.href); console.log(window.parent.location.href); } but ended up with an error Uncaught DOMException: Blocked a frame with origin "https://myurl.com" from accessing a cross-origin frame. at console.log (<anonymous>) at https://www.myurl.com/embed/1544401:20:21 Is there a workaround? UPD. Solved by using document.referrer
You can't access an <iframe> with different origin using JavaScript, it would be a huge security flaw if you could do it. For the same-origin policy browsers block scripts trying to access a frame with a different origin. you can work around this problem using window.postMessage and its relative message event to send messages between the two pages, like this: //in your main page const frame = document.getElementById('your-frame-id'); frame.contentWindow.postMessage(/*any variable or object here*/, 'http://your-second-site.com'); //In your <iframe> (contained in the main page): window.addEventListener('message', event => { // IMPORTANT: check the origin of the data! if (event.origin.startsWith('http://your-first-site.com')) { // The data was sent from your site. // Data sent with postMessage is stored in event.data: console.log(event.data); } else { // The data was NOT sent from your site! // Be careful! Do not use it. This else branch is // here just for clarity, you usually shouldn't need it. return; } }); This method can be applied in both directions, creating a listener in the main page too, and receiving responses from the frame. The same logic can also be implemented in pop-ups and basically any new window generated by the main page (e.g. using window.open()) as well, without any difference. or just disable the same origin policy in your browser
Can't execute JavaScript cross origin, cross frame [duplicate]
I am loading an <iframe> in my HTML page and trying to access the elements within it using JavaScript, but when I try to execute my code, I get the following error: SecurityError: Blocked a frame with origin "http://www.example.com" from accessing a cross-origin frame. How can I access the elements in the frame? I am using this code for testing, but in vain: $(document).ready(function() { var iframeWindow = document.getElementById("my-iframe-id").contentWindow; iframeWindow.addEventListener("load", function() { var doc = iframe.contentDocument || iframe.contentWindow.document; var target = doc.getElementById("my-target-id"); target.innerHTML = "Found it!"; }); });
Same-origin policy You can't access an <iframe> with different origin using JavaScript, it would be a huge security flaw if you could do it. For the same-origin policy browsers block scripts trying to access a frame with a different origin. Origin is considered different if at least one of the following parts of the address isn't maintained: protocol://hostname:port/... Protocol, hostname and port must be the same of your domain if you want to access a frame. NOTE: Internet Explorer is known to not strictly follow this rule, see here for details. Examples Here's what would happen trying to access the following URLs from http://www.example.com/home/index.html URL RESULT http://www.example.com/home/other.html -> Success http://www.example.com/dir/inner/another.php -> Success http://www.example.com:80 -> Success (default port for HTTP) http://www.example.com:2251 -> Failure: different port http://data.example.com/dir/other.html -> Failure: different hostname https://www.example.com/home/index.html:80 -> Failure: different protocol ftp://www.example.com:21 -> Failure: different protocol & port https://google.com/search?q=james+bond -> Failure: different protocol, port & hostname Workaround Even though same-origin policy blocks scripts from accessing the content of sites with a different origin, if you own both the pages, you can work around this problem using window.postMessage and its relative message event to send messages between the two pages, like this: In your main page: const frame = document.getElementById('your-frame-id'); frame.contentWindow.postMessage(/*any variable or object here*/, 'https://your-second-site.example'); The second argument to postMessage() can be '*' to indicate no preference about the origin of the destination. A target origin should always be provided when possible, to avoid disclosing the data you send to any other site. In your <iframe> (contained in the main page): window.addEventListener('message', event => { // IMPORTANT: check the origin of the data! if (event.origin === 'https://your-first-site.example') { // The data was sent from your site. // Data sent with postMessage is stored in event.data: console.log(event.data); } else { // The data was NOT sent from your site! // Be careful! Do not use it. This else branch is // here just for clarity, you usually shouldn't need it. return; } }); This method can be applied in both directions, creating a listener in the main page too, and receiving responses from the frame. The same logic can also be implemented in pop-ups and basically any new window generated by the main page (e.g. using window.open()) as well, without any difference. Disabling same-origin policy in your browser There already are some good answers about this topic (I just found them googling), so, for the browsers where this is possible, I'll link the relative answer. However, please remember that disabling the same-origin policy will only affect your browser. Also, running a browser with same-origin security settings disabled grants any website access to cross-origin resources, so it's very unsafe and should NEVER be done if you do not know exactly what you are doing (e.g. development purposes). Google Chrome Mozilla Firefox Safari Opera: same as Chrome Microsoft Edge: same as Chrome Brave: same as Chrome Microsoft Edge (old non-Chromium version): not possible Microsoft Internet Explorer
Complementing Marco Bonelli's answer: the best current way of interacting between frames/iframes is using window.postMessage, supported by all browsers
Check the domain's web server for http://www.example.com configuration for X-Frame-Options It is a security feature designed to prevent clickJacking attacks, How Does clickJacking work? The evil page looks exactly like the victim page. Then it tricked users to enter their username and password. Technically the evil has an iframe with the source to the victim page. <html> <iframe src='victim-domain.example'/> <input id="username" type="text" style="display: none;"/> <input id="password" type="text" style="display: none;"/> <script> //some JS code that click jacking the user username and input from inside the iframe... <script/> <html> How the security feature work If you want to prevent web server request to be rendered within an iframe add the x-frame-options X-Frame-Options DENY The options are: SAMEORIGIN: allow only to my own domain render my HTML inside an iframe. DENY: do not allow my HTML to be rendered inside any iframe ALLOW-FROM https://example.com/: allow specific domain to render my HTML inside an iframe This is IIS config example: <httpProtocol> <customHeaders> <add name="X-Frame-Options" value="SAMEORIGIN" /> </customHeaders> </httpProtocol> The solution to the question If the web server activated the security feature it may cause a client-side SecurityError as it should.
For me i wanted to implement a 2-way handshake, meaning: - the parent window will load faster then the iframe - the iframe should talk to the parent window as soon as its ready - the parent is ready to receive the iframe message and replay this code is used to set white label in the iframe using [CSS custom property] code: iframe $(function() { window.onload = function() { // create listener function receiveMessage(e) { document.documentElement.style.setProperty('--header_bg', e.data.wl.header_bg); document.documentElement.style.setProperty('--header_text', e.data.wl.header_text); document.documentElement.style.setProperty('--button_bg', e.data.wl.button_bg); //alert(e.data.data.header_bg); } window.addEventListener('message', receiveMessage); // call parent parent.postMessage("GetWhiteLabel","*"); } }); parent $(function() { // create listener var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent"; var eventer = window[eventMethod]; var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message"; eventer(messageEvent, function (e) { // replay to child (iframe) document.getElementById('wrapper-iframe').contentWindow.postMessage( { event_id: 'white_label_message', wl: { header_bg: $('#Header').css('background-color'), header_text: $('#Header .HoverMenu a').css('color'), button_bg: $('#Header .HoverMenu a').css('background-color') } }, '*' ); }, false); }); naturally you can limit the origins and the text, this is easy-to-work-with code i found this examlpe to be helpful: [Cross-Domain Messaging With postMessage]
There is a workaround, actually, for specific scenarios. If you have two processes running on the same domain but different ports, the two Windows can interact without limitations. (i.e. localhost:3000 & localhost:2000). To make this work, each window needs to change their domain to the shared origin: document.domain = 'localhost' This also works in the scenario that you are working with different subdomains on the same second-level domain, i.e. you are on john.site.example trying to access peter.site.example or just site.example document.domain = 'site.example' By explicitily setting document.domain; the browser will ignore the hostname difference and the Windows can be treated as coming from the 'same-origin'. Now, in a parent window, you can reach into the iframe: frame.contentWindow.document.body.classList.add('happyDev')
If you have control over the content of the iframe - that is, if it is merely loaded in a cross-origin setup such as on Amazon Mechanical Turk - you can circumvent this problem with the <body onload='my_func(my_arg)'> attribute for the inner html. For example, for the inner html, use the this html parameter (yes - this is defined and it refers to the parent window of the inner body element): <body onload='changeForm(this)'> In the inner html : function changeForm(window) { console.log('inner window loaded: do whatever you want with the inner html'); window.document.getElementById('mturk_form').style.display = 'none'; </script>
I experienced this error when trying to embed an iframe and then opening the site with Brave. The error went away when I changed to "Shields Down" for the site in question. Obviously, this is not a full solution, since anyone else visiting the site with Brave will run into the same issue. To actually resolve it I would need to do one of the other things listed on this page. But at least I now know where the problem lies.
I would like to add Java Spring specific configuration that can effect on this. In Web site or Gateway application there is a contentSecurityPolicy setting in Spring you can find implementation of WebSecurityConfigurerAdapter sub class contentSecurityPolicy(" script-src 'self' [URLDomain]/scripts ; style-src 'self' [URLDomain]/styles; frame-src 'self' [URLDomain]/frameUrl... ... .referrerPolicy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN) Browser will be blocked if you have not define safe external contenet here.
Open the start menu Type windows+R or open "Run Execute the following command. chrome.exe --user-data-dir="C://Chrome dev session" --disable-web-security
Unsafe JavaScript attempt to access frame with URL (same domain!)
From the page file://localhost/Users/pistacchio/dev/epress/catflow/test_html/index.html I have the following (coffeescript) code trying to access an iframe: $('#ipad-viewport iframe').bind 'load', () -> console.log $(this).contents().find('map') (This translates to the following javascript, but I don't think the issue relies here): (function() { $('#ipad-viewport iframe').bind('load', function() { return console.log($(this).contents().find('map')); }); }).call(this); I wait for the iframe page to be loaded and try to access an element within its body. I get the following error: Unsafe JavaScript attempt to access frame with URL file://localhost/Users/pistacchio/dev/epress/catflow/test_html/catalogo/catalog/intro.html from frame with URL file://localhost/Users/pistacchio/dev/epress/catflow/test_html/index.html. Domains, protocols and ports must match. Now, since the iframe is defined like this: <iframe src="file://localhost/Users/pistacchio/dev/epress/catflow/test_html/catalogo/catalog/intro.html" width="1024" height="768"></iframe> Aren't both my page and the iframe in the same domain, or file://localhost? Why am I experiencing this problem? Oh, if relevant, I'm testing this with Chrome 18.
file:/// URLs are subject to a slightly different javascript security policy to the normal same origin policy that applies to hosted content. In order to stop a saved web page from being able to read the entire contents of your disk, different files are seen as different origins. Just fire up a local server and host your content on that; you will fall back to the "standard" policy where origins are defined by domain/ip. If for some reason you can't run a web server, you may get some mileage out of the command line switch: --allow-file-access-from-files. I believe this has the affect of making all file:/// URLs to be defined as belonging to the same origin.
error : Permission denied to access property 'document'
How can I fix this message in Firefox? I am using an Iframe which has an anchor tag? I would like to get a reference to this anchor but i am getting this error when I am trying to access anchor: var frameWindow = document.getElementById('myIframe').contentWindow; var anchor = frameWindow.document.links[0]; //.getElementsByClassName('a'); anchor.onclick....
Relaxing the same-origin policy In some circumstances the same-origin policy is too restrictive, posing problems for large websites that use multiple subdomains. Here are four techniques for relaxing it: document.domain property If two windows (or frames) contain scripts that set domain to the same value, the same-origin policy is relaxed for these two windows, and each window can interact with the other. For example, cooperating scripts in documents loaded from orders.example.com and catalog.example.com might set their document.domain properties to “example.com”, thereby making the documents appear to have the same origin and enabling each document to read properties of the other. This might not always work as the port stored in the internal representation can become marked as null. In other words example.com port 80 will become example.com port null because we update document.domain. Port null might not be treated as 80 ( depending on your browser ) and hence might fail or succeed depending on your browser. Cross-Origin Resource Sharing The second technique for relaxing the same-origin policy is being standardized under the name Cross-Origin Resource Sharing. This draft standard extends HTTP with a new Origin request header and a new Access-Control-Allow-Origin response header. It allows servers to use a header to explicitly list origins that may request a file or to use a wildcard and allow a file to be requested by any site. Browsers such as Firefox 3.5 and Safari 4 use this new header to allow the cross-origin HTTP requests with XMLHttpRequest that would otherwise have been forbidden by the same-origin policy.[7] Cross-document messaging Another new technique, cross-document messaging allows a script from one page to pass textual messages to a script on another page regardless of the script origins. Calling the postMessage() method on a Window object asynchronously fires an "onmessage" event in that window, triggering any user-defined event handlers. A script in one page still cannot directly access methods or variables in the other page, but they can communicate safely through this message-passing technique. JSONP JSONP allows a page to receive JSON data from a different domain by adding a <script> element to the page which loads a JSON response from a different domain. The function call is the "P" of JSONP—the "padding" around the pure JSON, or according to some the "prefix". By convention, the browser provides the name of the callback function as a named query parameter value, typically using the name jsonp or callback as the named query parameter field name, in its request to the server, e.g., <script type="application/javascript" src="http://server2.example.com/Users/1234?jsonp=parseResponse"> </script> In this example, the received payload would be: parseResponse({"Name": "Foo", "Id": 1234, "Rank": 7});
If the iframe points to a different domain, you will get this error. This is an example of your browser preventing cross-site scripting: http://en.wikipedia.org/wiki/Cross-site_scripting
The error is due to the same-origin policy like explained in other answers. I post this answer as a workaround to execute JavaScript code in a web console. My comment suggesting to use Firebug CD command no longer works because Firebug is not supported anymore. But there is a similar feature in Firefox Developer Tools, you can switch the domain name by selecting the iframe context picker button like described here: https://developer.mozilla.org/en-US/docs/Tools/Working_with_iframes
How can I access a parent DOM from an iframe on a different domain?
I have a website and my domain is registered through Network Solutions (who I would not recommend). I'm using their Web Forwarding feature which allows me to "mask" my domain so that when a user visits http://lucasmccoy.com they are actually seeing http://lucasmccoy.comlu.com/ through an HTML frame. The advantages of this are that the address bar still shows http://lucasmccoy.com/. The disadvantages are that I cannot directly edit the HTML page in which the frame is owned. For example, I cannot change the page title or favicon. I have tried doing it like so: $(function() { parent.document.title = 'Lucas McCoy'; }); But of course this gives me a JavaScript error: Unsafe JavaScript attempt to access frame with URL http://lucasmccoy.com/ from frame with URL http://lucasmccoy.comlu.com/. Domains, protocols and ports must match. I looked at this question attempting to do the same thing except the OP has access to the other pages HTML whereas I do not. Is there anyway in JavaScript/jQuery to make a cross-domain request to the DOM when you don't have access to that domain? Or is this something browsers just will not let happen for security reasons.
No. Most browsers implement the same origin policy.