strapyourself.in and flouri.sh
Beating The Browser's Iframe Security
I recently ran into the problem of cross domain communication between iframes while working on RightCart. We wanted to be able to pop up a LightBox outside the cart iframe, but the event had to be triggered by something happening inside the iframe. To make matters worse, the parent frame could be any domain and the child frame was always RightCart. After some research, I found that DoJo and the Windows Live Team claimed to have done this using nested iFrames.
No one else who is using this technique is willing to describe EXACTLY how it is to be done, so here we go:
The situation:
- Parent frame is http://www.somedomain.com/somepath/somefile
- Child iframe is http://rightcart.com/cart
- Parent executes some javascript initially which we control
- Child is completely controlled by us, and wants to call functions in parent
How I did it:
The child knows the URL of the parent through the HTTP referer (sic) header. If it creates a "subchild" iframe within the child pointing to the same top level URL as the parent, then 1-way communication is possible. How? The child can set the location of the subchild (but not read the location) and the parent can read the location of subchild, as long as the domain is the same as the parent.
So how do you transmit messages on a URL? Use the hash!
The hash is the portion of the URL after the # sign. You can change that portion of the URL and not mess with the browser. Most importantly, the browser will not go make a new request if it already has the page, regardless of what the hash value is.
For example, I could send a message to the parent by setting the location of the subchild using document.getElementById('...').src to http://www.somedomain.com#message_is_here. The parent can get the hash of the subchild by executing some javascript like window.frames[0].frames[0].location.hash. Got it? In order to send more complex messages, you'll want to Base64 encode the contents of the hash.
As for synchronization, I gave the messages sequence numbers, so that the parent knew if they had already seen the message. I also set a limit to how often messages could be sent (every 500 ms in my case). So the child had to queue messages if they came in too quickly:
function sendJavascriptToParent(js) { rightcart_comm_queue[rightcart_comm_queue.length] = js; }
This is important, because there's only 1-way communication involved and the parent cannot tell the child that it is ready for the next message. I set both frames up at a 500 ms interval to update and check the message respectively. This is not 100% reliable, but if you're worried, try simply checking for messages twice as often as your minimum update delay.
Here's my code to check for messages in the parent:
function check_for_comm() { var rcFrame = null; var innerFrame = null; rcFrame = window.frames["rightcart"]; if (rcFrame) { innerFrame = rcFrame.frames[0]; if (innerFrame) { var loc = innerFrame.location; debug_print("InnerFrame location = "+loc.href); var frag = loc.hash.substr(1); var seqnum = frag.split("!")[0]; var data = frag.split("!")[1]; debug_print("SeqNum: "+seqnum+", Data="+data); if (data.length > 0 && seqnum != comm_last_seq) { comm_last_seq = seqnum; var decoded_data = decodeBase64(data); debug_print("Running: "+decoded_data); eval(decoded_data); } } } }
And my code to send a message from the child:
function doSend() { if (rightcart_comm_queue.length > 0) { var seq = new Date().getTime(); var ifr = document.getElementById('rightcart_comm_frame'); var all_js = ''; for(var i=0; i<rightcart_comm_queue.length; i++) { var js = rightcart_comm_queue[i]; all_js += js+'\\n'; } var data = encodeBase64(all_js); var url = rightcart_comm_baseurl+'#'+seq+'!'+data; /*alert('Sending Data: '+all_js+' using url '+url);*/ ifr.src = url; rightcart_comm_queue = []; } }
I had trouble using the EXACT same URL as the parent for the subchild. To solve this, I dissected the URL and set it to robots.txt in the same domain. This will cause a browser request for the first message.
Sorry, comments are closed for this article.