HTML5 Cross-Document Messaging Vulnerabilities

In this article, we take a look at the security model that the Web Messaging API (a.k.a. “Cross-Document Messaging”) – is built on, why the security measures that it introduces are necessary, and some of the potential mis-configurations that can undermine the API’s security model.

HTML5 Cross-Document Messaging Vulnerabilities

In this article, we’ll look at some of advanced capabilities that were delivered in the HTML5 specification between 2008 and 2014 to update the existing Hypertext Markup Language (HTML/XHTML) standard to improve the language with support for the latest multimedia and which introduced application programming interfaces (APIs) to support the development of complex web applications.

We’ll take a look at the security model that one of these new APIs – the Web Messaging API (a.k.a. “Cross-Document Messaging”) – is built on, why the security measures that it introduces are necessary, and some of the potential mis-configurations that can undermine the API’s security model. However in order to understand the full context, we’ll first look back at some of the historical context for the issues that have plagued web application security prior to HTML5, before looking at how the Web Messaging API is intended to address these.

 

Background / Primer – HTML & the DOM

Web pages are simple documents written in HTML, a simple form of markup that can be read “raw” as HTML source code, or else rendered and displayed in the browser window as a formatted multimedia document. The Document Object Model (DOM) is a way of representing the objects that comprise the structure and content of these documents as an addressable hierarchy of nodes and objects in a tree.

 

The Document Object Model (DOM)

 

This allows browsers to perform programmatic reading and writing of the document dynamically – programs such as web browsers can read the DOM to interpret the document structure, style, and content in a web page in a standard manner. It also permits scripts such as JavaScript that operate client-side to interact with and read or write these same elements, changing the page’s rendering and behaviour.

 

Mashups & Mixed Origins

One of the basic features of the web is that of interconnections and (relatively) open access to resources. This means that a web application served to a client from one origin or domain (e.g https://www.example.com) may return an HTML response containing a <script> tag instructing a client’s browser to load a resource from another domain, such as https://ajax.googleapis.com, a domain that may be unrelated and owned and operated by another individual.

 

Mashups & Mixed Origins Diagram

 

This functionality is important in that it enables many of the common functionalities we see on modern website and that the web depends up on, for example:

1. Allowing developers to create websites that load simple static resources such as images from an image hosting site or CDN;
2. Allowing a user to upload a profile picture on one website such as an ID service and then have the picture display on a different website such as a forum or social media site;
3. Allowing developers to make use of shared resources such as common open-source libraries and scripts that are located on provider websites; or
4. Allowing marketing teams to integrate content such as videos or adverts from a different provider on their own site.

This concept is basic to the web and is sometimes referred to as a “web mashup”.

 

Potential Dangers

Modern dynamic web applications offer rich functionality in which resources such as JavaScript are permitted to both access and potentially modify client-side data stored within the browser, via a hierarchical and addressable series of properties known as the Document Object Model (DOM). This includes access to authentication tokens that are stored within cookies. To prevent a malicious domain executing JavaScript to access the web application’s data a protection measure is needed to restrict the ability of such resources from different domains and zones of trust to access data stored on the client’s browser.

 

DOM cross-origin protection – The Same Origin Policy (SOP)

The “Same-Origin Policy (SOP)” was introduced by Netscape within their “Navigator 2.02” browser product way back in 1995 as a security concept to address the dangers outlined above, and is used within all major browsers today. It ensures segregation of data relating to different web applications by placing a series of restrictions on the interaction between resources loaded from different origins or domains. It was added largely in response to the introduction of JavaScript in Netscape 2.0, which enabled dynamic interaction with the DOM. Under the Same Origin Policy, a web browser restricts access to resources relating to one origin to being permitted only via other resources from that origin. This prevents a malicious script from one origin from obtaining access to sensitive data on a page from a different origin via that page’s Document Object Model.

The origin doesn’t entirely coincide with the domain alone – web resources (or more accurately the URLs that they load from) are determined to have the same origin if the protocol (e.g HTTP or HTTPS), port (e.g 80 or 443), and host (e.g https://www.example.com:443) are all the same for both resources. Note there are some exceptions to this in Internet Explorer but we will not be discussing those here.

The table below lists some examples compared the origin http://www.example.com to a variety of URLs.

 

[click to see larger image]

The way that the SOP is implemented means that cross-origin reads via scripts are disallowed by default.

This means that sites can still embed content from external sources – such as iframes to deliver adverts or YouTube videos mentioned in our examples above – but in such a way that they do not grant the serving site for the loaded material the ability to use scripts to read the DOM it is loaded within, or to view user interaction fired as actions within that DOM.

 

Malicious web page DOM

 

Additionally, a malicious web page loaded at https://evil.com can make HTTP requests (e.g using XMLHttpRequest) and read the response within its own origin, but cannot read responses from another origin such as https://www.example.com.
It should be noted that the HTTP request to a different origin will be sent, however the response cannot be read by the JavaScript.
It is also important to note that JavaScript embedded in an application will be executed within the context of that application’s origin, even if it was loaded from another domain. (See our blog posts on Cross-Site Scripting for more information on how this can impact your application).

 

Diagram of SOP blocking malicious http request

 

Problems for multiple domains

The Same Origin Policy therefore effectively prevents malicious scripts from accessing data from another domain (origin). Whilst the Same Origin Policy is an important and well tested security concept, there are some legitimate cases where two trusted websites need to exchange data between each other. Many modern applications described as “mashups” above require the ability to communicate across and load resources between multiple trusted origins in order to deliver rich functionality.
There are generally one of two requirements: either the application needs to send a cross-origin HTTP request and be able to read the response; or two documents from different origins need to exchange data within the user’s browser. We have discussed the former in one of our previous blog posts: “Secure inclusion of third party content using SOP, CSP, SRI & CORS.” The latter can be accomplished using HTML5 Web Messaging.

 

HTML5 Web Messaging

Before the introduction of web messaging the communication of different origins was restricted by the same origin policy and enforced by the browser. Developers were able to make use of several methods including JSONP as well as largely insecure ‘hacks’ involving external plugins such as ActiveX and Flash, but none of these were formally part of the HTML specification and many carried inherent risks of usage or flaws. To meet this need, Cross Document Messaging was introduced formally within the Web Hypertext Application Technology Working Group (WHATWG) HTML5 draft specification and implemented in all major browsers. It enables secure communication between multiple origins across iframes, tabs and windows. Web Messaging (also known as Cross Document Messaging) allows applications running on different domains to communicate in a secure manner.

 

How Web Messaging Works – The Messaging API

The Messaging API introduced the window.postMessage() method, with which plain-text messages can be sent cross-origin, providing a controlled mechanism to securely circumvent the same-origin policy restriction. The window.postMessage() method safely enables cross-origin communication between documents, e.g. such as between a page and a pop-up that it spawned, or between a page and an iframe embedded within it.

Broadly, one document may obtain a reference to another (e.g. via window.opener), and then dispatch a MessageEvent on it with targetWindow.postMessage().

For example a page may send a message to another window:

var contentWindow = window.open(“http://siteB.example.com”);
contentWindow.postMessage(“Hello world”, ”http://siteB.example.com”);

The “receiving” window also uses an event handler for the MessageEvent to process responses messages. For example:

function receiveMessage(event)
{
if(event.origin === ‘http://siteA.example.com’) {
event.source.postMessage("Hello back at you", event.origin);
}
}
window.addEventListener("message", receiveMessage, false);

 

 

Diagram showing How Web Messaging Works

 

Since both documents can send and receive messages using, this effectively enables developers to create a two-way communication channel using JavaScript that operates at the application layer to allow the transfer of data between scripts on two different domains. Note that it is also possible to use web messaging to send and receive messages from documents in the same origin using identical code.

Note that postMessage is not related to a HTTP message using the POST method, these are two distinct technologies with similar terminology.

 

Domain Trust & Origin Restriction

Receiving A Message
It is essential that code receiving messages implements validation and filtering of the origin and processes messages from trusted domains only. The most basic example of a vulnerability relating to origin restriction would be to simply fail to specify an origin check, which would accept input from any domain and undermine the securely model:

window.addEventListener(“message”, callback, true);
function callback(e) {
/* process message (e.data) */
}

 

Typically the best way to securely implement origin restriction is using a allow-list of permitted origins, as in the example below:

function receiveMessage(event)
{
// Do we trust the sender of this message?
if (event.origin !== "http://siteA.example.com")
// Untrusted!
return;
} else {
/ * process message */
}
}

 

It is also important to try and avoid the use of wildcards where possible in origin restriction. In the example below, the intention is to allow subdomains of our main example.com, ie all valid subdomains in the form *.example.com, such as:

• www.example.com
• static1.example.com
• static2.example.com

Some example code that might, naively, be written to implement this is shown below:

window.addEventListener(“message”, callback, true);
function callback(e) {
if(e.origin.indexOf(“.example.com”)!=-1) {
/* process message (e.data) */
}
}

 

The indexOf() method returns the position of the first occurrence of a specified value in a string, and returns -1 if the value to search for never occurs. So the code above will match any string containing “.example.com”. However this code is insecure, since the filter can be bypassed using a value such as www.example.com.appcheck-ng.com which an attacker would be able to register, yet will match (pass) the “indexOf” check that is designed to restrict the domains accepted to trusted origins only.

Another common error is to utilise a regular expression without correctly escaping wildcard characters and/or checking the line ends, for example:

 

window.addEventListener(“message”, callback, true);
function callback(e) {
var regex = /https*://www.example.com/i;
if (regex.test(event.origin)) {
/* process message (e.data) */
}
}

 

The regular expression in the previous code snippet fails to escape the wildcard “.” and does not have a “$” at the end to ensure that there are no trailing characters. Therefore the following domains will pass the validation and potentially be under the control of an attacker: https://wwwXexample.com, https://www.example.com.appcheck-ng.com

 

Sending a message
Another important consideration is the target origin of messages that are being sent. The default targetOrigin parameter when calling postMessage() is “/” which restricts the message to the same origin, however the wildcard “*” can be used which allows any origin to receive the message if it is sent it.

Since the postMessage() method must be called on a specific object (such as a Window), it may be believed that the wildcard can be safely used, however this is not the case. Even if the Window object was opened by the application to point to a trusted domain initially, navigation within that window may have caused the document to change to an untrusted domain, allowing the untrusted domain to receive the message.

A common error is to send messages with a wildcard targetOrigin to window.parent, operating under the assumption that only trusted domains can frame the document. This is assumption is often flawed as even if the document can only be framed by a trusted domain (which is not always the case), a malicious document can open a new window to the application rather than framing it.

Ensuring that a trusted targetOrigin is specified when sending a message is particularly important when the message contains sensitive information.

 

Input Validation

Input validation of the received data is also important, even where the origin is correctly validated and the website is therefore accepting messages from trusted domains only. That is, received data should be treated as potentially unsafe, regardless of whether the origin itself is validated and trusted, vulnerabilities in the trusted site may allow an attacker to send malicious messages which can lead to vulnerabilities including Cross-Site Scripting (XSS).

In the example below, the code directly updates the contents the HTML content of a page element with content received in a MessageEvent:

window.addEventListener(“message”, callback, true);
function callback(e) {
element.innerHTML= e.data;
}

 

This code results in a Cross-Site Scripting (XSS) vulnerability as the data is not being properly validated or sanitised.

 

Prevention & Mitigation

In order to provide maximum security, if you do not expect to receive messages from other sites, do not add any event listeners for message events. If you do expect to receive messages from other sites, then you should always be aware that malicious domains may attempt to send data to your API message handler within your page. You should ensure that your code always verifies the sender’s origin.

Likewise, it is equally important that you always specify an exact target origin, when you use postMessage to send data to other windows. A malicious site can potentially change the location of the window without your knowledge, and therefore it can intercept the data sent using postMessage; whitelisting target origins prevents this.

Finally, you should then always verify the syntax of the received message. Otherwise, a security flaw or malicious code in the site that you trusted to send only safe and properly formatted messages could open a cross-site scripting vulnerability in your own site.

 

How can AppCheck Help?

AppCheck help you with providing assurance in your entire organisation’s security footprint. AppCheck performs comprehensive checks for a massive range of web application vulnerabilities from first principle to detect vulnerabilities in in-house application code. AppCheck also draws on checks for known infrastructure vulnerabilities in vendor devices and code from a large database of known and published CVEs. The AppCheck Vulnerability Analysis Engine provides detailed rationale behind each finding including a custom narrative to explain the detection methodology, verbose technical detail and proof of concept evidence through safe exploitation.

 

Additional Information

As always, if you require any more information on this topic or want to see what unexpected vulnerabilities AppCheck can pick up in your website and applications then please get in contact with us: info@localhost.

 

Checkout our Hunting HTML5 PostMessage Vulnerabilities blog post for more information on identifying and exploiting Web Messaging Vulnerabilities.

Get started with Appcheck

No software to download or install.

Contact us or call us 0113 887 8380

About Appcheck

AppCheck is a software security vendor based in the UK, offering a leading security scanning platform that automates the discovery of security flaws within organisations websites, applications, network and cloud infrastructure. AppCheck are authorized by te Common Vulnerabilities and Exposures (CVE) Program aas a CVE Numbering Authority (CNA)

No software to download or install.
Contact us or call us 0113 887 8380

Start your free trial

Your details
IP Addresses
URLs

Get in touch