I am developing a system that allows userwritten javascript in widgets. To keep things secure, I plan to sandbox these widgets in iframes. Of course, for the sandboxing to be effective the iframe must have a different domain than the parent document.
I would really love to be able to dynamically generate the iframe with code similar to this:
template = '<html><body><script>/* user code */</script></body></html>'
src = 'javascript: document.write("' + template + '")'
widget = $('<iframe>').attr('src', src)
$('#container').append(widget)
...and then have the resulting iframe be treated as cross-domain from the parent window. Is this possible, and if so, how would it be done?
Ok, I think I get what you need but it's a bit tricky.
You want to create an <iframe>
and populate it with the user Javascript client-side but still have the client sandboxed?
This is fairly non-standard. Usually the contents of the <iframe>
are generated server side. But here it goes.
First some background: documents can not access the content of any document that is not from the same domain (including sub-domain) and port. But they can change their own security domain using the document.domain
property. So what you need to do is lighten up the security then tighten it back up again for the user script to run.
So you can't do it the way you specified because if you create an <iframe>
with a Javascript src
the document.domain
will match the parent frame. This means that the widget will have full access to everything.
So here's how you can do it:
- Set up two sub-domains of your main domain. Let's call them
home.example.com
and widgets.example.com
.
- Create a basic HTML file on
widgets.example.com
and make sure it calls this javascript: document.domain = "example.com";
- Now create your page that will contain all these widgets. Set it's
document.domain
to the same value.
- Create all your iframes loading your basic HTML page from
widgets.example.com
into it.
- Set a variable inside the frame that contains the user template. Ex:
myFrame.contentWindow.foo = "template";
- Switch the
document.domain
on the main window back to home.example.com
so that the <iframe>
s will no longer have access to the parent frame
- Trigger the template substitution in the frame
That last part is the tricky part. You can't just embed the code because if it runs automatically it will run before you can change the domain of the home document back, which will be a security issue. So instead you need to set it to a temporary variable inside the frame then somehow trigger the frame to replace its own contents with that template but only after everything is locked down. The easiest and most compatible way would be to trigger it on resize and then just change the width or height of the frame.
Now, alternatively, if the widget was populated server-side:
- Host widget on
widgets.example.com
- Host page containing widget on
home.example.com
- Done
But I assume that you have reasons for doing it all client-side.
The logical next topics: communicating between the frames and auto-sizing. But those are for another day.
Did I answer your question? I hope so because this was a lot of typing and I won't mind the reputation points if you vote up and accept my answer! ;)