Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

javascript - postMessage to sandboxed iframe, why is recipient window origin null?

2 postMessage calls in the test: 1 using an asterisk for targetOrigin, one using the same https url of both the parent and child documents.

button 1:

$('.iframed')[0].contentWindow.postMessage( messageData , '*' );

button 2:

$('.iframed')[0].contentWindow.postMessage( messageData , 'https://myurl.net' );

the iframe element in parent html document, which points to child html file on same domain, in same directory:

<iframe name="childFrame" class="iframed" src="child.html" sandbox="allow-scripts"></iframe>

both documents are fully loaded before I am clicking the buttons to trigger postMessage.

==========================================

with iframe element written as above, button 1 performs the postMessage to the child iframe and triggers the child's postMessage listener successfully (though it uses the asterisk for targetOrigin, which I'd prefer not to do.) however, button 2 results in the following error in console:

"Failed to execute ‘postMessage’ on ‘DOMWindow’: The target origin provided (‘https://myurl.net’) does not match the recipient window’s origin (‘null’)."

==========================================

if I add "allow-same-origin" to the iframe's sandbox parameters, both buttons then pass the postMessage data successfully (no "null" error on the button 2 postMessage call with the url provided for the targetOrigin.) however, I don't want to do this, as I am using the iframe's sandboxing behavior to block the iframe content from calling js functions in the parent document. this is for a system allowing "arbitrary" content (html/js/images/pdfs -- nothing server-executable like php though) to be loaded into the child iframe.

perhaps of note, similar buttons inside of the iframe content that postMessage to the parent document work just fine, regardless of the allow-same-origin parameter or the presence of the asterisk/url:

I framed button 1:

parent.postMessage( messageData , 'https://myurl.net' ); 

iframed button 2:

parent.postMessage( messageData , '*' ); 

==========================================

so, why does postMessage from the parent to the iframe results in the error above if I don't add "allow-same-origin" (and why does this issue not affect the iframe postMessage to the parent)? I attempted setting the iframe src to an absolute https url to the child.html document, but results were the same. I also tested the same code on a different, non ssl-cert server location, and had the same results (so don't think it is https contributing...). MUST i have EITHER the asterisk as the targetOrigin, AND/OR use allow-same-origin in the sandbox params?

other conversations about this issue on SO seem to dead-end so hoping to get a new perspective on a solution...

like image 437
Crite Avatar asked Jun 15 '17 11:06

Crite


People also ask

How do you send iframe to postMessage?

postMessage in your web app sends to the main document's window , not to the iframe's. Specify the iframe's window object: document. getElementById('cross_domain_page').

Why would you use the sandbox attribute with an iframe?

Applying the sandbox attribute to iframes you include allows you to grant certain privileges to the content they display, only those privileges which are necessary for the content to function correctly.

What is sandboxed iframe?

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.

What is window postMessage () used for?

postMessage() The window. postMessage() method safely enables cross-origin communication between Window objects; e.g., between a page and a pop-up that it spawned, or between a page and an iframe embedded within it.


1 Answers

The problem is created by the <iframe> itself, with its sandbox attribute:

<iframe name="childFrame" class="iframed" src="child.html" sandbox="allow-scripts"></iframe>

As per the Mozilla Developer Network documentation on this element, the following gives a problem about same-origin policy:

allow-same-origin: If this token is not used, the resource is treated as being from a special origin that always fails the same-origin policy.

You didn't specified allow-same-origin, that means the frame is treated as being from a special origin, and postMessage calls to that frame will fail.

To solve the problem, just add allow-same-origin to the sandbox attribute, like this:

<!-- index.html -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<iframe name="childFrame" class="iframed" src="child.html" sandbox="allow-scripts allow-same-origin"></iframe>
<button class="btn1">A</button>
<button class="btn2">B</button>

<script>
$('.btn1').click(function(event) {
    // This works!
    $('.iframed')[0].contentWindow.postMessage( "something" , '*' );
});
$('.btn2').click(function(event) {
    // This will work too
    $('.iframed')[0].contentWindow.postMessage( "whatever you want" , 'https://myurl.net' );
});
</script>
<!-- child.html -->
<script type="text/javascript">
    window.onmessage = function(event) {
        console.log(event.data);
    }
</script>

That's it!

like image 66
Maxime Launois Avatar answered Oct 04 '22 15:10

Maxime Launois