Web messages#

Web Messages (the postMessage API) is a browser mechanism that allows communication between different browsing contexts, like between a page and an <iframe>, or between a page and a Web Worker, or even across different origins.

Normally, the Same-Origin Policy blocks scripts from different origins from accessing each other’s data. postMessage provides a controlled, safe channel to pass data across that boundary.

If a page handles incoming web messages in an unsafe way, for example, not verify the origin of incoming messages correctly, we can pass our controlled message to the functions or properties that consume the message

Vulnerabilities#

No origin verification#

Consider the following code:

<!-- https://vulnerable-website.com/webmessage/path -->
<script>
window.addEventListener('message', function(e) {
  eval(e.data);
});
</script>

This is vulnerable because an attacker could inject a JavaScript payload by constructing the following iframe, hosting it on their own server:

<iframe src="https://vulnerable-website.com/webmessage/path" onload="this.contentWindow.postMessage('alert(origin)','*')">

As the event listener does not verify the origin of the message, and the postMessage() method specifies the targetOrigin "*", the event listener accepts the payload and passes it into a sink, in this case, the eval() function.

Note that the <iframe> can be on any website, including localhost, or file://test.html but the src attribute has to be pointing to the path which contains the vulnerable JavaScript code which handles incoming web messages

Insufficient origin verification#

Even if an event listener does include some form of origin verification, this verification step can sometimes be flawed. For example, consider the following code:

window.addEventListener('message', function(e) {
    if (e.origin.indexOf('normal-website.com') > -1) {
        eval(e.data);
    }
});

The indexOf method is used to try to verify that the origin of the incoming message is the normal-website.com domain.

However, in practice, it only checks whether the string "normal-website.com" is contained anywhere in the origin URL.

As a result, an attacker could easily bypass this verification by hosting the <iframe> tag on http://www.normal-website.com.evil.net domain

The same flaw also applies to verification checks that rely on the startsWith() or endsWith() methods.

For example, the following event listener would regard the origin http://www.malicious-websitenormal-website.com as safe:

window.addEventListener('message', function(e) {
    if (e.origin.endsWith('normal-website.com')) {
        eval(e.data);
    }
});