Using JavaScript and window.postMessage() for Safe Cross-Domain Communication

Using JavaScript and window.postMessage() for Safe Cross-Domain Communication

Cross-Domain communication (also called Cross-origin) can be difficult and pose security risks. 

As stated on 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.

So here is the senerio…

I want to communicate between two windows. I have two separate sites in domains abc[dot]com and xyz[dot]com respectively. There is a useful feature of window.postMessage(), which is safe if used correctly.

Usage:

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

Syntax 

targetWindow.postMessage(message, targetOrigin);

  • targetWindow is a handle to the window to which you want to send the message.
  • message data to be sent to the other window. The data is serialized using the structured clone algorithm and we can pass a broad variety of data objects safely to the destination window without having to serialize them yourself.
  • targetOrigin specifies what the origin of targetWindow must be for the event to be dispatched. 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.

If “*” is used as targetOrigin, the message could be from anyone. we can validate the receivers domain against the targetOrigin. If they do not match, do not accept the message. 

Example

We will create two web pages named controller.html and receiver.html. receiver.html will be a pop-up(open in a new window). Now, receiver.html will send data to controller.html and controller.html will send a response to receiver.html.

controller.html

<!DOCTYPE html>
<html>
    <head>
        <title></title>
        <meta charset="utf-8" />
        <script>
            function openUrl() {
                let inputVal = document.getElementById("myInput").value;
                let targetWindow = window.open(inputVal);
                let eventMethod = window.addEventListener
                    ? "addEventListener"
                    : "attachEvent";
                let eventer = window[eventMethod];
                let messageEvent =
                    eventMethod === "attachEvent" ? "onmessage" : "message";


                // Listen to message from child window
                eventer(
                    messageEvent,
                    function (e) {
                        // Check if origin is proper
                        if (e.origin !== "http://127.0.0.1:3000") {
                            return;
                        }


                        const obj = e.data;
                        const tbody = document.getElementById("tbody");


                        for (var i = 0; i < obj.length; i++) {
                            var tr = "<tr>";


                            /* Verification to add the last decimal 0 */
                            if (
                                obj[i].value
                                    .toString().substring(obj[i].value.toString().indexOf("."),obj[i].value.toString().length) < 2)
                                obj[i].value += "0";


                            /* Must not forget the $ sign */
                            tr += "<td>" +obj[i].key + "</td>" + "<td>$" + obj[i].value.toString() + "</td></tr>";


                            /* We add the table row to the table body */
                            tbody.innerHTML += tr;
                        }


                        // Here we are sending back response to receiver.html
                        var pathArray = inputVal?.split("/");
                        var url =
                            pathArray && pathArray[0] + "//" + pathArray[2];


                        targetWindow.postMessage( { access: true }, "http://127.0.0.1:3000/");
                    },
                    false
                );
            }
        </script>
    </head>
    <body>
        <form>
            <fieldset>
                <input
                    type="text"
                    placeholder="Type something..."
                    id="myInput"
                />
                <button type="button" onclick="openUrl();">open URL</button>
            </fieldset>
        </form>
        <table>
            <tbody id="tbody"></tbody>
        </table>
    </body>
</html>        


receiver.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>Popup</title>
        <script type="text/javascript">
            window.opener.postMessage(
                [
                    {
                        key: "apple",
                        value: 1.9,
                    },
                    {
                        key: "berry",
                        value: 1.7,
                    },
                    {
                        key: "banana",
                        value: 1.5,
                    },
                    {
                        key: "cherry",
                        value: 1.2,
                    },
                ],
                "http://127.0.0.1:3000/"
            );
            window.addEventListener(
                "message",
                function (e) {
                    // Check if origin is proper
                    if (e.origin != "http://127.0.0.1:3000") {
                        return;
                    }
                    document.getElementById(
                        "demo"
                    ).innerHTML = `Access: ${e?.data?.access}`;
                },
                false
            );
        </script>
    </head>
    <body>
        <p id="demo"></p>
    </body>
</html>
        


Test Run

Open controller.html and enter the URL of receiver.html. Now the receiver.html will popup and it will send data to the controller.html

No alt text provided for this image

Once controller.html gets the data it will send back response to receiver.html

No alt text provided for this image

What is happening?

When we entered the URL of receiver.html, an object has been sent back to the controller.html from receiver.html using the reference(window.opener.postMessage()). Again the controller sent a response to the receiver.html using the reference of the popup window(targetWindow.postMessage()).

  • The message is contained in e.data.
  • The targetOrigin is contained in e.origin.

So, window.postMessage() is a safe way to send messages between windows in different domains or origins.

Useful Links




Amarjeet Sarma

I write code - For work; for fun.

2y

Ae bey ki suka bey

Like
Reply

To view or add a comment, sign in

Insights from the community

Others also viewed

Explore topics