Secure coding

HTML5 Security: Cross Domain Messaging

Srinivas
March 9, 2015 by
Srinivas

In this article, we will discuss HTML5 Web Messaging (or Cross Domain Messaging) attack vectors and security implementations.

Why is it important to understand HTML5 attacks?

Learn Secure Coding

Learn Secure Coding

Build your secure coding skills in C/C++, iOS, Java, .NET, Node.js, PHP and other languages.

HTML5 is one of the emerging technologies for next generation Web applications. It has brought a lot of new features to the Web. HTML5 applications are also widely used in the mobile app world. Along with the features, HTML5 has brought various new attack vectors as well. The main focus of this article is to show the possible attack vectors with the Cross Domain Messaging feature.

Before going ahead with the security concepts of Cross Domain Messaging, let us understand the basics of how Cross Domain Messaging is implemented in HTML5.

Cross Domain Messaging

Due to the same origin policy restrictions before HTML5, sending messages between windows was only possible if both windows used the same protocol, port, and host.

With the introduction of HTML5, all those restrictions are gone and we can now pass messages across domains without having to worry about Same Origin Policy restrictions.

HTML5 has a new method called postMessage(). Using this, we can pass messages between windows regardless of their origin.

Below is the syntax of postMessage().

Sending Window:

[plain]

otherWindow.postMessage(message, targetOrigin, [transfer]);

[/plain]

otherWindow: This is a reference to another window.

Message: The message to be passed to the receiving window.

targetOrigin: The URL of the receiving window must be specified here. If we do not have any specific preference, we can specify it as "*". Specifying "*" as 'targetOrigin' has some security implications we will discuss in later sections of this article.

Transfer: This is optional.

Receiving Window:

When otherWindow.postMessage() is executed, a messageEvent will be dispatched at the receiver window.

We can receive the message dispatched by the sender using the following code snippet.

[plain]

window.addEventListener("message",receiveMessage,

false);

function receiveMessage(event){

if

(event.origin !== "http://site.com:8383")

return;

//
...

}
[/plain]

From the above code snippet, we can access the data and origin of this message as shown below.

event.origin – Gives the origin of the message (The URI from which we are receiving this message).

event.data – Gives the actual message being sent.

Now, we have got some basic knowledge of what cross domain messaging in HTML5 is and how it is implemented in the applications.

Let us now see the security implications of cross domain messaging.

For demonstration purposes, I have set up the following lab.

A: http://localhost:8383/

B: http://localhost/

As we can see, we have two different ports on the above two URLs. The first URL is running on port 8383 and the second URL is on the default port 80. So, it is obvious that they have two different origins, since the port numbers are different.

In our lab setup, A is the message sender and B is the receiving window.

We are going to load the second URL http://localhost/ as an iframe in the first URL.

I can send messages from the domain http://localhost:8383/ to the domain http://localhost/ using the postMessage method.

We can check it by clicking the "Send Message" button as shown below.

The iframe which is loaded into the first URL is from a different origin, but we are able to send a message to it using HTML5's postMessage() method.

Now, let us look at some scenarios where this postMessage() implementation can introduce vulnerabilities into our applications.

Case 1

Code at sender:

[plain]

receiver.postMessage('Hi There..!', '*');<

[/plain]

When the sender has the above code where he specifies the target origin with a wildcard "*", an unintended recipient (window) can receive this message from the sender. Since the receiving window is listening for incoming messages, anyone can load it into an iframe and can listen for the messages coming to it. So, it is a bad idea to give a wildcard when passing sensitive data to the receiving windows.

How to fix this:

It is possible to fix this just by adding the specific target in the target field. So, in this case http://localhost is the only origin that can receive this message.

This is as shown below.

[plain]

receiver.postMessage('Hi There..!', 'http://localhost');

[/plain]

Case 2

Code at receiving window:

[plain]
function receiveMessage(e) {

do something..!

}

[/plain]

In the above code, we are receiving the message from the sender and directly processing it without checking who sent this message.

It is always important to check the origin of the message to prevent receiving messages from unauthorized senders.

How to fix this:

[plain]
function receiveMessage(e) {

if (e.origin !== "http://localhost:8383")
return;

do something..!

}

[/plain]

Always validate the origin from which you want to receive the messages. In our case, we want to receive messages only from http://localhost:8383. So, we are making a simple check to see if the message is coming from http://localhost:8383 using the property event.origin. If this is not matching, we won't receive the message.

Case 3

The next attack vector is the infamous Cross Site Scripting. Both the sender as well as receiver should always validate the messages being passed. If the data is inserted into HTML DOM without proper validation, then the application becomes vulnerable to DOM based Cross Site Scripting.

The following code snippet shows how an application may become vulnerable when a malicious message is received from the attacker and it is inserted into the receiver's HTML DOM using innerHTML property.

Sender:

[plain]

receiver.postMessage("<img src='x' onerror=alert(1);>", 'http://localhost');

[/plain]

Receiver:

[plain]
function receiveMessage(e) {

if (e.origin !== "http://localhost:8383")
return;

messageEle.innerHTML = "Message from localhost:8383: " + e.data;

}

[/plain]

When the above code is executed, it causes an XSS in the receiving window as shown in the figure below.

How to fix this:

The easiest way to fix this issue is to assign the data value to an element using textContent rather than using innerHTML.

This is done as shown below.

Sender:

[plain]

receiver.postMessage("<img src='x' onerror=alert(1);>", 'http://localhost');

[/plain]

Receiver:

[plain]
function receiveMessage(e) {

if (e.origin !== "http://localhost:8383")
return;

element.textContent = "Message from localhost:8383: " + e.data;

}

[/plain]

When the above code is executed, we should see the text displayed in the receiving frame as "data" rather than code.

As we can see in the above figure, the code is now not executed. Rather, it is displayed as normal text.

Conclusion

Learn Secure Coding

Learn Secure Coding

Build your secure coding skills in C/C++, iOS, Java, .NET, Node.js, PHP and other languages.

We have discussed the basics of Cross Domain Messaging and some of the possible attacks against this feature in HTML5. We will discuss other possible attacks against HTML5 web applications in later articles.

Srinivas
Srinivas

Srinivas is an Information Security professional with 4 years of industry experience in Web, Mobile and Infrastructure Penetration Testing. He is currently a security researcher at Infosec Institute Inc. He holds Offensive Security Certified Professional(OSCP) Certification. He blogs atwww.androidpentesting.com. Email: srini0x00@gmail.com