circuit

Using JavaScript and window.postMessage()

Safe Cross-Domain Communication




Photo by Kate Macate on Unsplash

Introduction

Cross-Domain communication (also called Cross-origin) can be difficult and pose security risks. However, there is a useful and often overlooked feature of HTML5, window.postMessage(), which is safe if used correctly.

As stated on the MDN,

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.

In this article, we will focus on communication** between windows** and not a window and an iframe.

By the way, the statement on the MDN says pop-up window, which we will use, but the recipient window does not have to be a pop-up.

Syntax

The two required components and their syntax

The two **required **components are,

  1. window.postMessage() — to send the message

  2. window.addEventListener(“message”,callback) — to receive and process the message

The syntax for the postMessage() method is,

*targetWindow*.postMessage(*message*, *targetOrigin*, [*transfer*]);

There is a optional third parameter, [transfer], that we will not be using. You may want to read more about on the MDN.

  • **targetWindow **is a handle to the window to which you want to send the message.

  • **message **can be quite a few complex objects. However, functions cannot be sent as part of the message as the message data is serialized using the structured clone algorithm. The structured clone algorithm does not allow for functions. However, this does mean you can pass a broad variety of data Objects safely.

  • **targetOrigin **is a very important piece. It is the URI **of the recipient **page. At the moment of dispatch (postMessage), if the targetOrigin, does match the host name of targetWindow’s page, it will fail to send. It is possible to use “” as the targetorigin, but only do that for simple testing (as we will do here.)

To stress this further, in production, on the receiving end, you will want to validate the receivers domain against the targetOrigin. If they do not match, do not accept the message.

Beware: If “” is used as targetOrigin, the message could be from anyone.

Keep in mind, we are typically sending from one domain to another, so targetOrigin must match the domain of the targetWindow listener.

In other words, we may want to send a message from https://abcd.com to https://defg.com. So targetOrigin would be https://defg.com. and the recipient’s domain would be https://defg.com as well.

Example

We will create two web pages named Page1.html and Page2.html. Page2.html will be a pop-up, it does not have to be, this is just my choice.

Page1.html will send a message to Page2.html.

Pages

Create the following files

Page1.html (note the sendMessage function)

<!DOCTYPE html>
<html>
<head>
    <title></title>
 <meta charset="utf-8" />
<script>

var childwin;
const childname = "popup";
function openChild() {

childwin = window.open('Page2.html', childname, 'height=300px, width=500px');

}
function sendMessage(){
    let msg={pName : "Bob", pAge: "35"};
    // In production, DO NOT use '*', use toe target domain
    childwin.postMessage(msg,'*')// childwin is the targetWindow
    childwin.focus();
}

</script>
</head>
<body>
    <form>
        <fieldset>
            <input type='button' id='btnopen' value='Open child' onclick='openChild();' />
            <input type='button' id='btnSendMsg' value='Send Message' onclick='sendMessage();' />
        </fieldset>
    </form>
</body>
</html>

Page2.html (note callback function in addEventListener)

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <meta charset="utf-8" />
    <script>

// Allow window to listen for a postMessage
    window.addEventListener("message", (event)=>{

// Normally you would check event.origin
        // To verify the targetOrigin matches
        // this window's domain
        let txt=document.querySelector('#txtMsg');
        // event.data contains the message sent
        txt.value=`Name is ${event.data.pName} Age is  ${event.data.pAge}` ;

    });

function closeMe() {
        try {

window.close();
        } catch (e) { console.log(e) }
        try {

self.close();
        } catch (e) { console.log(e) }

}
    </script>
</head>
<body>
    <form>
        <h1>Recipient of postMessage</h1>
            <fieldset>
                <input type='text' id='txtMsg' />
                <input type='button' id='btnCloseMe' value='Close me' onclick='closeMe();' />
            </fieldset>

    </form>
</body>
</html>

Test Run

Open Page1.html and click the “Open child” button. This opens the pop-up.

Popup-targetWindowPopup-targetWindow

Click the “Send Message” button. The message is received by the pop-up window.

Result of childwin.postMessage()Result of childwin.postMessage()

What is Happening?

When “Send Message” is clicked, an Object is being sent to the recipient page using the reference to that window, childwin, created during window.open().

childwind.postMessage(msg,””)

Again, DO NOT use “” other than for testing.

The targetOrigin should be the domain in which the recipient page resides.

The Event Listener of the recipient window receives the message in a parameter named “event”.

The callback function, an Arrow function in our case, processes the message.

  • The message is contained in event.data.

  • The targetOrigin is contained in event.origin.

Conclusion

window.postMessage() is a safe way to send messages between windows in different domains or origins. One can also post to an IFrame.

The data being sent is serialized using the structured clone algorithm and will accept almost any type of simple or complex data.

There is also a postMessage() that can be used when the browser context is the same. This involves a channel.

channel.postMessage() will be covered in another article.

Thank you for reading and happy coding!

Read all the articles you want on Medium and help me keep writing by becoming a Medium member for only $5/month. Join Medium with my referral link — gravity well (Rob Tomlin) *As a Medium member, a portion of your membership fee goes to writers you read, and you get full access to every story…*bobtomlin-70659.medium.com

You may also enjoy, Using JavaScript and The Broadcast Channel API For Browser Context Communication Between Tabsmedium.com Introduction to JavaScript’s Geolocation API What JavaScript’s Geolocation API is and how to use itmedium.com Closing a Window with JavaScript What works, what doesn’t, in a nutshellmedium.com




Continue Learning