postMessage giving "Blocked frame" error, but only in Safari - javascript

I am using postMessage to share data between https://www.example.com (Which I'll call mainSite) and https://subdomain.example.com (which I'll call subSite)
subSite has an iframe to mainSite,
subSite's code looks something like:
//set the src of the iframe
$("#main-site-iframe").attr("src", "https://www.example.com");
//wait for the main site page to open
$("#main-site-iframe")[0].onload = () => {
//my page posts a message to their site
$("#my-iframe")[0].contentWindow.postMessage("messagePlox", "https://www.example.com");
};
//wait for the iframe to message back
window.addEventListener('message', iframeResponse, false);
function iframeResponse(e) {
//make sure the request is from the correct site
if(e.origin == 'https://www.example.com')
{
//Got the data
console.log(e.data);
}
}
mainSite looks like this:
//listen for the subdomain to make a request
window.addEventListener('message', subdomainRequest, false);
function subdomainRequest(e) {
//make sure the request is from the correct subdomain
if(e.origin == 'https://subdomain.example.com')
{
//respond with the data
e.source.postMessage("We got you", e.origin);
}
}
The problem with the above is that it works in every browser EXCEPT Safari which refuses it and says:
Blocked a frame with origin "https://www.example.com" from accessing a frame with origin "https://subdomain.example.com". Protocols, domains, and ports must match.
Which seems to imply the response back is breaking since mainSite is trying to "access" subSite.
Does anyone know why this would only happen in Safari and not in Firefox or Chrome?

Two-way cross-domain iFrame communication is usually blocked in Safari/Opera. The primary way around this is using a gateway that the parent and child both agree on... but they cannot using IPC use network-less messages to communicate bidirectionally. Alternatively, There are some exceptions that are closely guarded by the Nitro JS engine team, it appears if you have a large iFrame and the user is interacting with it, the messages from the parent will go through, and vise versa.
Messages that come from the parent get handled as if they are in a SW... that is to say don't expect things to work normally in the handler... test a lot. My promise resolve function for instance, can't be called from a message handling function for some reason... Which means I have to check to see if the function has been called in a time out, making things, painfully slow.
If you just need to show a success message from the child, you could do this:
parent.sendSuccessMsg();
function sendSuccessMsg(){
document.getElementById('iframeID').src="http://example.com/?successData=123";
}
Otherwise, you can use websockets with a gateway to mingle cross-domain. There seems to be some sort of undocumented exception if you load the iFrame twice, and the message is sent early on iframe load.
Source: https://benohead.com/blog/2015/12/07/cross-document-communication-with-iframes/
Bug/Failsafe Discovery: how to send cross domain post message in safari?

Related

How can I cancel consecutive requests to my server? [duplicate]

What would cause a page to be canceled? I have a screenshot of the Chrome Developer Tools.
This happens often but not every time. It seems like once some other resources are cached, a page refresh will load the LeftPane.aspx. And what's really odd is this only happens in Google Chrome, not Internet Explorer 8. Any ideas why Chrome would cancel a request?
We fought a similar problem where Chrome was canceling requests to load things within frames or iframes, but only intermittently and it seemed dependent on the computer and/or the speed of the internet connection.
This information is a few months out of date, but I built Chromium from scratch, dug through the source to find all the places where requests could get cancelled, and slapped breakpoints on all of them to debug. From memory, the only places where Chrome will cancel a request:
The DOM element that caused the request to be made got deleted (i.e. an IMG is being loaded, but before the load happened, you deleted the IMG node)
You did something that made loading the data unnecessary. (i.e. you started loading a iframe, then changed the src or overwrite the contents)
There are lots of requests going to the same server, and a network problem on earlier requests showed that subsequent requests weren't going to work (DNS lookup error, earlier (same) request resulted e.g. HTTP 400 error code, etc)
In our case we finally traced it down to one frame trying to append HTML to another frame, that sometimes happened before the destination frame even loaded. Once you touch the contents of an iframe, it can no longer load the resource into it (how would it know where to put it?) so it cancels the request.
status=canceled may happen also on ajax requests on JavaScript events:
<script>
$("#call_ajax").on("click", function(event){
$.ajax({
...
});
});
</script>
<button id="call_ajax">call</button>
The event successfully sends the request, but is is canceled then (but processed by the server). The reason is, the elements submit forms on click events, no matter if you make any ajax requests on the same click event.
To prevent request from being cancelled, JavaScript event.preventDefault(); have to be called:
<script>
$("#call_ajax").on("click", function(event){
event.preventDefault();
$.ajax({
...
});
});
</script>
NB: Make sure you don't have any wrapping form elements.
I had a similar issue where my button with onclick={} was wrapped in a form element. When clicking the button the form is also submitted, and that messed it all up...
Another thing to look out for could be the AdBlock extension, or extensions in general.
But "a lot" of people have AdBlock....
To rule out extension(s) open a new tab in incognito making sure that "allow in incognito is off" for the extention(s) you want to test.
In my case, I found that it is jquery global timeout settings, a jquery plugin setup global timeout to 500ms, so that when the request exceed 500ms, chrome will cancel the request.
You might want to check the "X-Frame-Options" header tag. If its set to SAMEORIGIN or DENY then the iFrame insertion will be canceled by Chrome (and other browsers) per the spec.
Also, note that some browsers support the ALLOW-FROM setting but Chrome does not.
To resolve this, you will need to remove the "X-Frame-Options" header tag. This could leave you open to clickjacking attacks so you will need to decide what the risks are and how to mitigate them.
Here's what happened to me: the server was returning a malformed "Location" header for a 302 redirect.
Chrome failed to tell me this, of course. I opened the page in firefox, and immediately discovered the problem.
Nice to have multiple tools :)
Another place we've encountered the (canceled) status is in a particular TLS certificate misconfiguration. If a site such as https://www.example.com is misconfigured such that the certificate does not include the www. but is valid for https://example.com, chrome will cancel this request and automatically redirect to the latter site. This is not the case for Firefox.
Currently valid example: https://www.pthree.org/
A cancelled request happened to me when redirecting between secure and non-secure pages on separate domains within an iframe. The redirected request showed in dev tools as a "cancelled" request.
I have a page with an iframe containing a form hosted by my payment gateway. When the form in the iframe was submitted, the payment gateway would redirect back to a URL on my server. The redirect recently stopped working and ended up as a "cancelled" request instead.
It seems that Chrome (I was using Windows 7 Chrome 30.0.1599.101) no longer allowed a redirect within the iframe to go to a non-secure page on a separate domain. To fix it, I just made sure any redirected requests in the iframe were always sent to secure URLs.
When I created a simpler test page with only an iframe, there was a warning in the console (which I had previous missed or maybe didn't show up):
[Blocked] The page at https://mydomain.com/Payment/EnterDetails ran insecure content from http://mydomain.com/Payment/Success
The redirect turned into a cancelled request in Chrome on PC, Mac and Android. I don't know if it is specific to my website setup (SagePay Low Profile) or if something has changed in Chrome.
Chrome Version 33.0.1750.154 m consistently cancels image loads if I am using the Mobile Emulation pointed at my localhost; specifically with User Agent spoofing on (vs. just Screen settings).
When I turn User Agent spoofing off; image requests aren't canceled, I see the images.
I still don't understand why; in the former case, where the request is cancelled the Request Headers (CAUTION: Provisional headers are shown) have only
Accept
Cache-Control
Pragma
Referer
User-Agent
In the latter case, all of those plus others like:
Cookie
Connection
Host
Accept-Encoding
Accept-Language
Shrug
I got this error in Chrome when I redirected via JavaScript:
<script>
window.location.href = "devhost:88/somepage";
</script>
As you see I forgot the 'http://'. After I added it, it worked.
Here is another case of request being canceled by chrome, which I just encountered, which is not covered by any of answers up there.
In a nutshell
Self-signed certificate not being trusted on my android phone.
Details
We are in development/debug phase. The url is pointing to a self-signed host. The code is like:
location.href = 'https://some.host.com/some/path'
Chrome just canceled the request silently, leaving no clue for newbie to web development like myself to fix the issue. Once I downloaded and installed the certificate using the android phone the issue is gone.
If you use axios it can help you
// change timeout delay:
instance.defaults.timeout = 2500;
https://github.com/axios/axios#config-order-of-precedence
For my case, I had an anchor with click event like
<a href="" onclick="somemethod($index, hour, $event)">
Inside click event I had some network call, Chrome cancelling the request. The anchor has href with "" means, it reloads the page and the same time it has click event with network call that gets cancelled. Whenever i replace the href with void like
<a href="javascript:void(0)" onclick="somemethod($index, hour, $event)">
The problem went away!
If you make use of some Observable-based HTTP requests like those built-in in Angular (2+), then the HTTP request can be canceled when observable gets canceled (common thing when you're using RxJS 6 switchMap operator to combine the streams). In most cases it's enough to use mergeMap operator instead, if you want the request to complete.
I had faced the same issue, somewhere deep in our code we had this pseudocode:
create an iframe
onload of iframe submit a form
After 2 seconds, remove the iframe
thus, when the server takes more than 2 seconds to respond the iframe to which the server was writing the response to, was removed, but the response was still to be written , but there was no iframe to write , thus chrome cancelled the request, thus to avoid this I made sure that the iframe is removed only after the response is over, or you can change the target to "_blank".
Thus one of the reason is:
when the resource(iframe in my case) that you are writing something in, is removed or deleted before you stop writing to it, the request will be cancelled
I have embedded all types of font as well as woff, woff2, ttf when I embed a web font in style sheet. Recently I noticed that Chrome cancels request to ttf and woff when woff2 is present. I use Chrome version 66.0.3359.181 right now but I am not sure when Chrome started canceling of extra font types.
We had this problem having tag <button> in the form, that was supposed to send ajax request from js. But this request was canceled, due to browser, that sends form automatically on any click on button inside the form.
So if you realy want to use button instead of regular div or span on the page, and you want to send form throw js - you should setup a listener with preventDefault function.
e.g.
$('button').on('click', function(e){
e.preventDefault();
//do ajax
$.ajax({
...
});
})
I had the exact same thing with two CSS files that were stored in another folder outside my main css folder. I'm using Expression Engine and found that the issue was in the rules in my htaccess file. I just added the folder to one of my conditions and it fixed it. Here's an example:
RewriteCond %{REQUEST_URI} !(images|css|js|new_folder|favicon.ico)
So it might be worth you checking your htaccess file for any potential conflicts
happened to me the same when calling a. js file with $. ajax, and make an ajax request, what I did was call normally.
In my case the code to show e-mail client window caused Chrome to stop loading images:
document.location.href = mailToLink;
moving it to $(window).load(function () {...}) instead of $(function () {...}) helped.
In can this helps anybody I came across the cancelled status when I left out the return false; in the form submit. This caused the ajax send to be immediately followed by the submit action, which overwrote the current page. The code is shown below, with the important return false at the end.
$('form').submit(function() {
$.validator.unobtrusive.parse($('form'));
var data = $('form').serialize();
data.__RequestVerificationToken = $('input[name=__RequestVerificationToken]').val();
if ($('form').valid()) {
$.ajax({
url: this.action,
type: 'POST',
data: data,
success: submitSuccess,
fail: submitFailed
});
}
return false; //needed to stop default form submit action
});
Hope that helps someone.
For anyone coming from LoopbackJS and attempting to use the custom stream method like provided in their chart example. I was getting this error using a PersistedModel, switching to a basic Model fixed my issue of the eventsource status cancelling out.
Again, this is specifically for the loopback api. And since this is a top answer and top on google i figured i'de throw this in the mix of answers.
For me 'canceled' status was because the file did not exist. Strange why chrome does not show 404.
It was as simple as an incorrect path for me. I would suggest the first step in debugging would be to see if you can load the file independently of ajax etc.
The requests might have been blocked by a tracking protection plugin.
It happened to me when loading 300 images as background images. I'm guessing once first one timed out, it cancelled all the rest, or reached max concurrent request. need to implement a 5-at-a-time
One the reasons could be that the XMLHttpRequest.abort() was called somewhere in the code, in this case, the request will have the cancelled status in the Chrome Developer tools Network tab.
In my case, it started coming after chrome 76 update.
Due to some issue in my JS code, window.location was getting updated multiple times which resulted in canceling previous request.
Although the issue was present from before, chrome started cancelling request after update to version 76.
I had the same issue when updating a record. Inside the save() i was prepping the rawdata taken from the form to match the database format (doing a lot of mapping of enums values, etc), and this intermittently cancels the put request. i resolved it by taking out the data prepping from the save() and creating a dedicated dataPrep() method out of it. I turned this dataPrep into async and await all the memory intensive data conversion. I then return the prepped data to the save() method that i could use in the http put client. I made sure i await on dataPrep() before calling the put method:
await dataToUpdate = await dataPrep();
http.put(apiUrl, dataToUpdate);
This solved the intermittent cancelling of request.

Cross-origin security error when moving an application to a subdomain (2018)

Background information: We have a platform which runs on https://system.example.com. This platform consists of 10 separate web applications (all written in PHP and JS). Each application has historically been in a sub-directory within the same subdomain:
https://system.example.com/app1/
https://system.example.com/app2/
...
https://system.example.com/app10/
We are in the process of rebuilding one of the applications, app2, and have decided to host this on a new separate subdomain, https://app2.example.com.
Part of the app2 application uses JavaScript to open a pop-up window for app10. Most functionality inside this popup works as expected. However, when attempting to use a "Save" button inside the popup my browser console was showing:
Uncaught DOMException: Blocked a frame with origin "https://app2.example.com" from accessing a cross-origin frame.
at https://system.example.com/app10/manage.php:1:334
I have read both SecurityError: Blocked a frame with origin from accessing a cross-origin frame and https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage but still unclear as to how to fix this.
The code and process I have is as follows:
The popup is opened from https://app2.example.com by a button which has an onclick event handler:
<button onclick="postToPopUp('https://system.example.com/app10/manage.php', 'fileManage', 'width=800px,height=600px', ['f_id', '123'], 'app2', 'filesCallbackManage')">Open app10</button>
The postToPopup() function is used to pass POST data from app2 into https://system.example.com/app10/manage.php based on Javascript window.open pass values using POST - this works fine.
The problem occurs when I click a "Save" button inside the popup which renders the following markup within the popup window:
<!doctype HTML><html><head><title>upload</title>
<script type="text/javascript" language="javascript" charset="utf-8">
var fileObject = {"files":{"0":{"f_id":"1784","f_title":"test07.pdf"},"f_id":123}};
window.opener.filesCallbackManage(fileObject);
window.close();
</script><body></body></html>
What this did originally - when everything was under the same subdomain - was called a js function filesCallbackManage() which resided in the code for https://system.example.com/app2. The function itself was passed an object, fileObject, which updated various parts of the UI inside app2. The popup was closed after clicking the Save button due to window.close();
Although I've read about using postMessage I don't understand how this fits in or whether this is even the correct solution to my problem? The data is being posted from the subdomain https://app2.example.com to https://system.example.com/app10 correctly. The problem is that filesCallbackManage() won't fire because of the cross origin restriction. Inside my code for https://app2.example.com I have a simple statement to see if it's firing:
function filesCallbackManage(data)
{
console.log('filesCallbackManage has fired');
}
This never fires because of the problem I have. I get the console error mentioned previously and a blank popup window (technically this is correct since there is nothing in the <body> tag in the above markup) but the window doesn't close and the callback isn't fired.
The example given on the Mozilla website isn't extensive enough to understand how it can be adapted to this type of scenario. Please can someone elaborate? Furthermore, the linked Stack Overflow post is four years old so I want to be sure anything I put on this is secure and up-to-date.
The postToPopup() function is used to pass POST data
Submitting a form across origins is fine. So you can do this.
The problem occurs when I click a "Save" button inside the popup
You're trying to access the DOM of the window across origins. This is forbidden.
Although I've read about using postMessage I don't understand how this fits in or whether this is even the correct solution to my problem?
postMessage is as close as you can get to accessing the DOM of a window across origins.
You can't do this.
var fileObject = {"files":{"0":{"f_id":"1784","f_title":"test07.pdf"},"f_id":123}};
window.opener.filesCallbackManage(fileObject);
Instead you have to send a message:
window.opener.postMessage(fileObject, "https://system.example.com");
And have code which listens for it:
addEventListener("message", receiveMessage);
function receiveMessage(event) {
if (event.origin !== "http://app2.example.com") { return false; }
filesCallbackManage(event.data);
}

How to cancel webRequest silently in chrome extension

I'm re-posting my question from Chromium-extensions google group here.
In my extension, I want to cancel some webRequests based on url pattern. My problem is that, if I return {cancel:true} in the onBeforeRequest event listener, the browser would redirect to a page telling me that the request is blocked by some extension. But I just want to cancel the request silently(as nothing happened).
I have also tried to return {redirectUrl:""} in the onBeforeRequest event listener, the console would log an error saying that "" was not a valid URL, and a bar appeared at the bottom of the browser, saying "Waiting for extension". To dismiss that bar, I then run content script "window.stop()" in that web page. That works sometimes, but not always. So I wonder if someone has any better solution. Thanks!!
You should use "javascript:" url instead :
{
redirectUrl:"javascript:"
}
return {redirectUrl: 'javascript:void(0)'};
Redirect to a page which replies with HTTP status code 204, e.g. https://robwu.nl/204 This is my website, and I do not log any traffic to this URL.
The specification demands the following behavior for a response with HTTP status 204:
If the client is a user agent, it SHOULD NOT change its document view from that which caused the request to be sent. This response is primarily intended to allow input for actions to take place without causing a change to the user agent's active document view, although any new or updated metainformation SHOULD be applied to the document currently in the user agent's active view.
Here is a simple example, an extension that silently blocks all requests to YouTube:
chrome.webRequest.onBeforeRequest.addListener(function(details) {
var scheme = /^https/.test(details.url) ? 'https' : 'http';
return { redirectUrl: scheme + '://robwu.nl/204' };
}, {
urls: ['*://www.youtube.com/*'] // Example: Block all requests to YouTube
}, ['blocking']);
This example redirects to http://robwu.nl/204 or https://robwu.nl/204 depending on the requests's scheme, to avoid mixed content warnings.
To get this example to work, you need to declare the webRequest, webRequestBlocking and host permissions for the site in the manifest file.

How can I read & write data to a new tab in Firefox?

In the past this used to work:
// writing
var newTab = window.open();
newTab.document.write("<html><head><title>I'm just a tab</title></head>");
newTab.document.write("<body id='hello'>With some text on it</body>");
newTab.document.write("</html>");
newTab.document.close();
// reading what was wrote
newTab.document.getElementById('hello').addEventListener("click", custom_search_function(), false);
However now when I try to execute this code, Firefox mentions a security error:
Error: SecurityError: The operation is insecure.
I searched the forum for an alternative and this works:
var textOnPage = "<html><head><title>I'm just a tab</title></head><body>";
var newTab = window.open("data:text/html;charset=UTF-8," + encodeURIComponent(textOnPage));
newTab.document.close();
But I can't access the page via getElementById
newTab.document.getElementById('hello').addEventListener("click", custom_search_function(), false);
returns:
Error: TypeError: newTab.document.getElementById(...) is null
How can I write to this new tab and then go back to read it through functions such as getElementById?
You're falling foul of the Single Origin Policy. When you open a new window without a URL, by definition it can't have the same domain name as the original (opener) window.
You could instead have the window.open() call open another URL on your site (mostly blank html) and as part of its body.onload event handler (or jQuery.ready()) you could set up an event handler for the message event like this:
$(document).ready(function(){
window.addEventListener("message", receiveMessage, false);
});
function receiveMessage(evt)
{
if (evt.origin !== "https://your-domain.here")
return;
// do something with evt.data
$(document.body).append(""+evt.data);
}
In your originating window you call:
otherWindow.postMessage(message, "https://your-domain.here");
The postMessage API is now well supported across a variety of modern browsers.
You'll still not be able to directly reach in to maniuplate the content of otherWindow, but you can post messages back from otherWindow to your originating window to achieve the same effect. (e.g: put your content manipulation code in otherWindow's content and 'call' it from your originating window).
This used to work in a variety of browsers for a long time (in my past life as a web developer I actually took advantage of this technique for web applications). It used to be that window.open pages were treated mostly like they were from the same domain as the parent page (although the behavior was always a little weird and the url in the url bar was usually about:blank).
Now the standard is to open the pages with the url origin being about:blank, and while I can understand why it made sense to simplify things and I can understand the reason this breaks the previous behavior, but the issue here is that about:blank is not a real url so the standard cross-origin rules should be different, at least in the case of window.open calls.
Is there a real security threat here from allowing windows to write to pages they have opened? I do not think so, but it is possible. In the end, I am no longer a web developer and do not care that much about these things, but I would not be surprised if other people's applications broke as well.

Issue with javascript permission denied from iframe, even when it is the same domain

I am having the eternal battle against same origin policy.
I have a registration process loaded dynamically to a page in an iframe. This process is on a different domain which I know is prohibited via the same origin policy.
However ! at the end of the process I direct the frame back to a page on the parent domain with a url variable and then fire a function to refresh the page.
I assumed that as the page is now on the same domain this would not be an issue and on firefox and chrome it works great, however on ie it still throws a permission denied error.
The function im running inside the iframe
if(gup("command") == "refresh"){
parent.samRefresh();
}
And in the parent frame I have
function samRefresh(){
location.reload(true);
}
Does any one have any ideas? I know that there are some scripts knocking around to do this however I would prefer not to use them if possible.
Thanks in advance for any help you can give me
Try the following:
In iFrame:
window.parent.success_msg(deferred);
On parent page:
window.success_msg = function(dfd) {
console.log("I'm from the parent window");
};
Also make sure you're running localhost rather than viewing your files from your file system.

Categories

Resources