Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problems with window.postMessage on Chrome

I have been stuck on this for hours.

I have a.html on http://example.com that contains an iframe with src to b.html on http://subdomain.example.com. a.html has some JS code to postMessage to the iframe.

The code to postMessage is simple:

iframe_window.postMessage('message', iframe_element.src)

But this way, Chrome throws an error:

Unable to post message to http://subdomain.example.com. Recipient has origin null.

I have also tried:

iframe_window.postMessage('message', 'http://subdomain.example.com')

But NO LUCK!

This is the ONLY WAY it works:

iframe_window.postMessage('message', '*')

But I have heard '*' is not good to use.

No problems in Firefox.

like image 514
user1437328 Avatar asked Sep 28 '12 13:09

user1437328


People also ask

What is window opener postMessage?

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.

What is the difference between SendMessage and postMessage?

SendMessage: Sends a message and waits until the procedure which is responsible for the message finishes and returns. PostMessage: Sends a message to the message queue and returns immediately.

Is window postMessage secure?

Security-Reviewing Uses of postMessage()postMessage is generally considered very secure as long as the programmer is careful to check the origin and source of an arriving message. Acting on a message without verifying its source opens a vector for cross-site scripting attacks.


1 Answers

It looks like this might be an issue with the child iframe not being loaded at the time the signal is sent, thus iframe.src doesn't have the proper value.

I did some testing and got the same error as you, but when I wrapped the postMessage call in a setTimeout and waited 100ms then there was no error, which tells me that this is an initialisation race condition.

Here's how I implemented a cleaner solution without the setTimeout:

Parent:

window.addEventListener("DOMContentLoaded", function() {

    var iframe = document.querySelector("iframe")
      , _window = iframe.contentWindow

    window.addEventListener("message", function(e) {

        // wait for child to signal that it's loaded.
        if ( e.data === "loaded" && e.origin === iframe.src.split("/").splice(0, 3).join("/")) {

            // send the child a message.
            _window.postMessage("Test", iframe.src)
        }
    })

}, false)

Child:

window.addEventListener("DOMContentLoaded", function() {

    // signal the parent that we're loaded.
    window.parent.postMessage("loaded", "*")

    // listen for messages from the parent.
    window.addEventListener("message", function(e) {

        var message = document.createElement("h1")

        message.innerHTML = e.data

        document.body.appendChild(message)

    }, false)

}, false)

This is a simple solution in which the child will signal to anyone that it's loaded (using "*", which is okay, because nothing sensitive is being sent.) The parent listens for a loaded event and checks that it's the child that it's interested in that's emitting it.

The parent then sends a message to the child, which is ready to receive it. When the child gets the message it puts the data in an <h1> and appends that to the <body>.

I tested this in Chrome with actual subdomains and this solution worked for me.

like image 194
Luke Channings Avatar answered Sep 16 '22 21:09

Luke Channings