Enabling cross-origin communication between Window objects
You are on a vacation. And your journey takes you to what you think is an uninhabited island. But you soon realise that there are people there. But the issue now is that they don’t understand a word of what you are saying. And neither can you.
We interact(communicate) with multiple people throughout our life. These interactions happen sometimes with people of our country(same-origin) and sometimes of different countries(cross-origin). So how do we communicate with each other if we don’t have a common language? The simplest answer can be ‘Using some kind of an audio translator’.
Now, a similar kind of scenario comes in the case of web application(s) as well, particularly applications having iframe(s) embedded within them. For these different window objects to interact with each other or exchange some information, there needs to be some kind of translator/communicator function. Let’s say based on some information from Window object 2, Window object 1 wants to perform some action. How do we handle this programmatically? The postMessage function of HTML 5 helps enable this communication between different window objects. Through this blog, I’ll walk you through all the nuts and bolts of the postMessage API through the following:
- Understanding the same-origin policy
- Understanding the postMessage API
- Implementing the postMessage API
- Points to remember while implementing this API
So, let’s board the Whatfix learning rocket-ship to learn about this.
Understanding the same-origin policy
It’s a policy that restricts resource sharing between window objects belonging to different domains. In layman’s terms, if an Indian citizen wants to cast a vote in the US presidential election or vice versa, he/she would not be allowed to do so. Citizenship is the policy here that restricts an outside individual(belonging to a different origin) to cast a vote.
A web page can embed iframes belonging to any origin without any restrictions. As a security constraint, the same-origin policy helps the browser to restrict the interactions of documents or scripts being loaded from another origin, thereby, not allowing malicious applications to access the data or executing scripts without user permissions.
Two URLs have the same origin if the protocol, port(if specified), and the host are the same for both.
To identify if two window objects belong to same-origin or cross-origin, run one of the following commands in your browser console:
document.querySelector('<iframe_selector>').contentDocument
(OR) document.querySelector('<iframe_selector>').contentWindow.document
If any of the commands above return a document reference, then both the window objects belong to the same-origin. If a null value is returned, then both the window objects belong to cross-origin.
Understanding the postMessage API
First, let’s understand the syntax of this API
targetWindow.postMessage(message, targetOrigin, [transfer]);
- targetWindow: It is the reference of the window object where the message needs to be sent.
- message: It contains the data that needs to be sent to the targetWindow.
- targetOrigin: It indicates what the origin of targetWindow must be for the event to be dispatched. It ensures that if any of the protocol, port, or the host of targetWindow doesn’t match the protocol, port, or the host as provided in targetOrigin, then the event is not dispatched. This is to avoid any malicious application to access sensitive information like passwords, etc. It can accept the following values:
- “*”: This means that event can be dispatched to any targetWindow as specified without the targetOrigin check.
- “http://abc.com:8001”: This means that event will only be dispatched to targetWindow having the same protocol, port, and host as specified.
- transfer: This is an optional parameter used to specify a sequence of transferable objects that get transferred with the message. Upon doing so, the ownership is transferred to the destination side and is no longer usable on the sending side.
The targetWindow can listen for the dispatched messages by attaching the following event listener in its window:
Now let’s understand the properties of dispatched event:
- event: It is the MessageEvent that represents the message received by the targetWindow. This object consists of the below properties:
- origin: The origin of the window that has sent the message. It consists of the protocol, port, and host of the source window. It can be used as a safety check at the targetWindow to ensure that the message is being sent by a trusted source.
- data: It is the message object passed from the source window that called postMessage API.
- source: It contains the reference of the window object that sent the message. This can be used to establish two-way communication.
Implementing the postMessage API
We’ll understand how we can use this API for both one-way and two-way communication:
One-way communication:
Given below is our main.html page
Our main.html is trying to send a message to iframe.html. So, let’s initialise an event listener of type message so that iframe.html can listen to the dispatched events.
Given below is our iframe.html page
Once the message event is dispatched, iframe.html first compares the event.origin to ensure the message is received through a trusted source and then processes the received message in an alert box.
Two-way communication:
For enabling two-way communication, we need to initialise listeners at both main.html and iframe.html page. Let’s change our script tag as displayed below:
Here our main.html sends a postMessage to the window of iframe.html. Iframe.html upon receiving the message validates the origin and then sends postMessage to the source window along with the page_info object. Source window processes the received message in an alert box.
This is how we can enable communication between different window objects using the browser’s native postMessage API.
Points to remember while implementing this API
- The sender window never knows if the target window has been redirected to some different origin. So it is always recommended at the sender side to provide targetOrigin to ensure messages are received only by trusted origins.
- Initialising message type event listeners enables the window to listen for any message. The receiver side must always validate the origin of the message event to ensure messages are being sent by trusted sources.
- Sending several postMessage APIs back and forth might end up slowing down an application. To avoid slowing down the application, always ensure to send a postMessage only if the sender and destination window objects belong to cross-origin and not otherwise.
Conclusion
I hope this information helped you learn about communications between different window objects and how to make them more secure while implementing postMessage API.
At Whatfix, we use postMessage API for some of our features to interact with iframes. For example, we are using the Whatfix editor to capture steps. As soon as we try to add a new step we start listening for events(like mouse-over, etc.) happening within an iframe using the postMessage API. As the cursor hovers around different elements we show the engagers as shown below:
Now that you understand how we can communicate even without a common language, your vacations might get more interesting!
See you in the next blog! Happy Coding!