Sandboxing JavaScript Using <iframe>
I’ve been experimenting with running code in an iframe
and the results are very encouraging.
Sandbox.eval()
I’m currently developing a templating system in JavaScript and ran into a problem with scope. The problem is that my template scripts have access to all of the global (window
) object’s properties and methods. I don’t want this. I want my template scripts to run in a separate, closed environment. Template scripts shouldn’t be able to address anything in the browser window. This could potentially lead to disaster.
Basically, I want the following code to work:
sandbox.eval("alert('Hello!')"); // => Hello! try { var goodbye = "Goodbye!"; sandbox.eval("alert(goodbye)"); // => ERROR! } catch (error) { alert("ERROR!"); }
The second eval
should fail because goodbye
is defined in the global scope but not in the templating environment. A standard eval
would work as the code is evaluated in the same scope that goodbye
is defined.
So I hacked around with an iframe
and eval
for a couple of hours and came up with this:
// create an <iframe> var iframe = document.createElement("iframe"); iframe.style.display = "none"; document.body.appendChild(iframe); // write a script into the <iframe> and create the sandbox frames[frames.length - 1].document.write( "<script>"+ "var MSIE/*@cc_on =1@*/;"+ // sniff "parent.sandbox=MSIE?this:{eval:function(s){return eval(s)}}"+ "<\/script>" );
I won’t bore you with the details. There is a browser sniff there because Internet Explorer handles eval
slightly different to other browsers. The important thing is that the code above allows us to evaluate JavaScript outside of the current and global scope.
In my next post I’ll show you some more iframe
tricks, including a way to subclass the JavaScript Array
object.
Comments (40)
Leave a comment
Comment: #1
[…] Previous post: ← Sandboxing JavaScript Using <iframe> […]
Comment: #2
useless
Comment: #3
Sandboxing could be very useful for Unit Testing of Javascript code where each unit test or unit test case runs in its own sandbox.
Comment: #4
Hey Dean, Nice hack. I look forward to a re-usable package
What are your thoughts on using this technique to allow safer parsing/eval of JSON strings? I’m not sure if it’s possible, but it seems like a good application.
Also, is there any concerns with memory leaks, having all these object references across iframes?
Comment: #5
Hi Dean,
I’ve made a script that makes tries to simulate the iframe.onload behavior which is not present in IE browsers, maybe it’s useful. See it at http://solutoire.com/index.php?resources=iframe…
cheers Bas
Comment: #6
Maybe obvious to some, but you should add document.open and document.close so avoid having the throbber spinning for ever.
Comment: #7
[…] I’m currently developing a templating system in JavaScript and ran into a problem with scope. The problem is that my template scripts have access to all of the global (window) object’s properties and methods. I don’t want this. I want my template scripts to run in a separate, closed environment. Template scripts shouldn’t be able to address anything in the browser window. This could potentially lead to disaster.Continue for more info…. PLAIN TEXT JAVA: […]
Comment: #8
If I get that right, the script you wright into that iframe returns the iframe itself for MSIE and a eval function that calls eval for every other browser. Therefore a call to sandbox.eval calls the eval directly on the iframe in MSIE and a wrapper function for others.
Is there any way to evaluate in global context for everybrowser? So far I know that eval.call(window, data) doesn’t work for IE and Safari. IE provides window.execScript as a workaround, which is fine. The only solution is to use window.setTimeout(data, 0) to evaluate in Safari in global context. But the timeout is asynchronous, therefore introducing new problems.
Any idea how to eval in Safari without timeout in global scope?
Comment: #9
I think that Dean is using here for IE the same feature that was causing troubles in jQuery. Knowing that IE eval-uates loaded scripts without getting access to the global context, Dean is using the iframe in IE only as if it were an ajaxed tag, ie bypassing his own hack.
On the other side, browsers which do get access to the global context are shielded by the iframe, because eval-uation takes place inside the window-like object of the iframe by means of the Dean’s closure-like hack: a sandbox is made equal to an object defined in the iframe, with just the needed eval function.
Comment: #10
You should read: …On the other side, in browsers which allow loaded scripts access the global context, these scripts are shielded…
Comment: #11
[…] But I’ve been playing with iframes. […]
Comment: #12
[…] Sandboxing JavaScript Using <iframe> – could be a useful hack for my work in future. […]
Comment: #13
Dean, I’ve also been using the iframe trick to get JavaScript code to run in its own global context. It’s a great way to prevent code that modifies global objects from stepping on other code.
If all you need is to prevent inadvertent access to the window or document object, that’s good enough. But if you are really trying to prevent all access, it doesn’t seem like it will do it. In your example:
What prevents the sandboxed code from doing this:
Comment: #14
Hi, dean, what is your templating system for? If u just want to wrap the codes, why not use new Function(code)() instead of eval(code) ? It’s also possible to forbid some symbols, for example new Function(‘parent’, code)(null) will forbid property parent.
I use this tech to make a package/import system.
Comment: #15
I need to use Iframe with document.write() to dynamically generate the html containing the media player depending on whether QT/WMP/Real is selected. Can you please help?
Comment: #16
Something I just discovered which I think is relevant: in IE, it seems that for an iframe window object there is no eval method. But if you call execScript on the iframe window just once (e.g. iframeWin.execScript(“”)) then the eval method magically appears. Tested and true in IE5 and 6 in Windows (for me, at least). So I have the following workaround in my code:
Comment: #17
My above example is slightly wrong – IE won’t let you execute the empty string as script, so the example should read something like
Comment: #18
[…] (PS: Having found the magic term execScript, I was then able to find some related articles on this topic by Dean Edwards and Jeff Watkins. However much of the details are buried in the comments, so I hope this article will increase both the findability and conciseness of this information). […]
Comment: #19
This is not exactly about sandboxing, but rather a response to Jörn Zaefferer’s question about how to “eval” code in Safari in the global scope synchronously. In fact, the function below works in every modern browser I’ve seen and in every platform: IE, Firefox, Mozilla, Safari, Camino, Opera etc; Mac, Windows, Linux etc.
Comment: #20
Hi Dean,
Nice post… yet another testament to IE being brain dead.
Another interesting way of avoiding global namespace pollution is to use a Function object and `with’. This isn’t the same as your sandbox in that the global scope is still visible, but it allows you to declare non-global variables and to manipulate the scope visible inside the function body from outside the function; consider:
Here `wizard’ appears in scope from the perspective of the function body. The above example is a bit contrived, but the real usefulness can be seen when used in conjunction with code libraries fetched using XMLHttpRequest. Suppose that we had a method called `debug’ which we would like to be available to all libraries loaded this way:
So now in ‘/jslib/crypt.js’ we can freely call `debug(…)’ as if it were global and furthermore, so as long as the code in this file is declaring variables with `var’, then these will not pollute the global namespace.
Cheers.
Comment: #21
@Dean
Nice work man, great idea!!! I am working on a library of my own, but was not happy with having all my classes polluting the global scope. Building an object tree for simulating namespaces was an improvement, but still I wasn’t very happy with it. And suddenly the light shines on the horizon…
@Michael Geary
As in your example: nothing. Except that it will propably be his own code that gets evaluated, so why bother. And setting parent to null in the iframe script would cut off all attempts :).
@Erik
Works like a charm… except when trying to get some return values from eval. Only the pure unaltered eval choice in that function provides. All others (execScript, timeout, script tag) do eval, admitted, but they are crippled. Half the times I ever used eval was with a return value, so…
@Jörn Zaefferer
Just use eval() instead of eval.call(). Even browsers from the stone age could do that :).
Some further thoughts on the comments of Erik and Jörn:
All those workarounds for something I really can’t imagine the use for. What’s wrong with just using eval when needed? I can follow Dean’s intentions with using eval outside the global scope. But why on earth is it so important in what scope eval runs otherwise? Because if it is to make sure something gets created in a specific scope, there are far better ways to go than eval. Even with arbitrary values:
and NOT
For those who think I don’t get my scopes… ah well, think whatever you like ;). I just meant that I don’t see the point in using eval.call(window,…) from within a function. Never met a situation where I needed it. Can’t really come up with one also. Come to think of it, there aren’t that many situations where one really needs eval at all.
Dean’s example is one worthy use for it. And he didn’t need a lengthy function to ‘fix’ (what’s in a word…) it. But in all, there are far more interesting ways for using the iframe technique, as demonstrated by the man himself with the Array2 example (next post).
Comment: #22
Thanks for such a great idea! I am currently working on project where application have to execute untrusted user scripts. There was a complex idea of using narcissus to run scripts in some secure environment.
However I’ve found bug with your implementation:
This throws an error in mozilla, but runs ok in IE.
Here is a fixed version:
safe_eval
function is public. It runs in ie6 and mozilla 1.5, I haven’t tested different browsers yet.Comment: #23
Oops… Mozilla evaluates everything in parent scope now
Comment: #24
Sorry for flooding…
Error described above is solved by calling
window.eval
instead ofeval
in mozilla. Your code could be fixed like this:Comment: #25
[…] Sandboxing JavaScript Using (tags: javascript sandbox iframe articles) […]
Comment: #26
@Stefan Van Reeth: When you have huge JS framework, and loading it all
onload
slows browser to crawl, you have to load strings andeval
uate them to speed things up (read: to make site work again). Look at Dojo and its loader system for example.Comment: #27
hey this better work or imma be pissed! nothing else is working
Comment: #28
[…] globalEval javascript function A function that works in every browser for evaluating JavaScript code in the global namespace. Oddly enough, it’s in a comment attached to Dean Edwards’ blog post about sandboxing code so that it’s *not* in the global space. (tags: javascript programming) […]
Comment: #29
[…] to SQL,ADO,C# – Environment SpecialFolder Usage in C# – Common StringBuilder Mistake in C# .NET – Sandboxing JavaScript Using <iframe> – MayHorizontal JavaScript Accordion 1kb – 40 Tips for optimizing your php code – Modern JavaScript […]
Comment: #30
Thanks ! Very nice code, perfect for my actual needs. Merci !
Comment: #31
Even if a script is placed inside an iframe, it can still access any object in the document using the window.parent object.. Am I getting this issue right?
Comment: #32
One bad problem with execScript is that if there is an error, then in IE6 you only get “Could not complete the operation due to error 80020101″. You lose all information about the original error. If you don’t have access to the machine, then you can’t work out why the problem occurred (e.g. prevent you from diagnosing the cause of an installation specific fault to a customer – which happens on occasion with IE)
[PS: If you are reading this because you have got that error, then try replacing
execScript(code);
with(window.eval || eval)(code, null);
!]Try it out using:
javascript:execScript(’syntaxerror;’);
And see if you get an 80020101 error (this behaviour may be specific to particular versions or patches of IE).Comment: #33
[…] in the web browser. Silly, I know. This is made possible by a wonderful hack of using iframes (http://dean.edwards.name/weblog/2006/11/sandbox/) to provide a fresh JS environment to each new “program”, along with a couple of basic […]
Comment: #34
I recently had a problem with parsing a large amount of JSON data to be converted to a table. The JSON-to-table function I’m using creates the table as an XHTML string and innerHTMLs it to the body. The resulting string is so large it was causing a “script stack space quota exhausted” error in firefox 3.0.5 and 3.1b2 (not sure about other versions). I’m using deans technique to get around this:
As far as I can tell this will only be a temporary workaround as the script stack space quota is reached in the iframe as the data I’m using grows. I just thought I’d share my use of the technique.
Comment: #35
i would like to know how to achieve the javascripts on the iframes. ondragstart, oncopy, ondblclick, etc are not avoilabel on iframes. Particularly, i want to disable the right click on the pdf which is displayed on the iframe. Please help.
URGENT
Comment: #36
Uh nithya… in case you haven’t noticed, PDF’s are displayed using the Adobe plugin.
Comment: #37
WTF
Comment: #38
So I know it’s been ages since a post on this topic, but given the current hotness of way to get CommonJS modules running in the browser, I thought of this topic once more.
In fact, I’m creating a little script that wraps up the functionality in this article into a nice little API that can be built on top of. Check it out if you care:
http://github.com/TooTallNate/SandboxJS
Comment: #39
[…] 注1:http://dean.edwards.name/weblog/2006/11/sandbox/注2:http://jsfiddle.net/注3:http://blog.csdn.net/aimingoo/archive/2009/09/08/4532496.aspx […]
Comment: #40
I may have a use for this inside of jPaq. If I do use it, I will definitely give you credit.
Comments are closed.