Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to execute a function through postMessage

I have this code inside a iframe:

window.addEventListener('message', function(e){

  if(e.data == 'test')
    console.log(e);

}, false);

and this inside the parent document:

$('#the_iframe').get(0).contentWindow.postMessage('test', 'http://localhost/');

So the parent document sends a "test" message to the iframe and it works.

But how can I define a function in the parent document, and somehow send this function through postMessage to the iframe, which will execute the function locally?

The function does some changes to the document like this:

var func = function(){
  $("#some_div").addClass('sss');
}

(#some_div exists in the iframe, not the parent document)

like image 570
Alex Avatar asked Jun 12 '12 21:06

Alex


People also ask

What is window postMessage () used for?

postMessage() 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.

What does postMessage return?

postMessage() sends a message back to the main page. The two ends can listen to messages from the other using window. addEventListener("message", (event) => {...}) .

What is Targetorigin postMessage?

postMessage method allows different windows or iframes to communicate directly, even if they were loaded from different origins, circumventing the usual same-origin policy. The sender of the message can restrict the origin of the receiver by specifying a target origin.


1 Answers

There's nothing that would prevent you from passing a stringified function as postmessage event data. Implementation is trivial, for any function declaration like

function doSomething(){
    alert("hello world!");
}

You could encodeURI its string interpretation:

console.log(encodeURI(doSomething.toString()));
//function%20doSomething()%20%7B%0A%20%20%20%20alert(%22hello%20world!%22);%0A%7D

It can then be executed as part of a closure - something not overly imaginative like

eval('('+decodeURI(strCallback)+')();');

There's a fiddle'd proof of concept without the cross-frame architecture - I'll see if I can put together a postMessage version, but it would be non-trivial to host w/jsfiddle

Update

As promised, a full mockup that works (links below). With correct event.origin checks this would be sufficiently inpenetrable, but I know for the fact that our security team would never let eval into production like this :)

Given the option I'd suggest the functionality be normalized across the two pages so that only a parametric message would need to be passed (i.e. pass arguments not functions); however there are definitely a few scenarios where this is a preferred approach.

Parent code:

document.domain = "fiddle.jshell.net";//sync the domains
window.addEventListener("message", receiveMessage, false);//set up the listener

function receiveMessage(e) {
    try {
        //attempt to deserialize function and execute as closure
        eval('(' + decodeURI(e.data) + ')();');
    } catch(e) {}
}

Iframe code:

document.domain = "fiddle.jshell.net";//sync the domains
window.addEventListener("message", receiveMessage, false);//set up the listener

function receiveMessage(e) {
    //"reply" with a serialized function
    e.source.postMessage(serializeFunction(doSomething), "http://fiddle.jshell.net");
}

function serializeFunction(f) {
    return encodeURI(f.toString());
}

function doSomething() {
    alert("hello world!");
}

Prototype mockup: parent code and iframe code.

like image 65
Oleg Avatar answered Nov 08 '22 09:11

Oleg