Search code examples
javascriptsecurityvirtual-machinexss

How to safely run user-supplied Javascript code inside the browser?


Imagine a scenario where I want to continuously invoke user-supplied Javascript code, like in the following example, where getUserResult is a function that some user (not myself) has written:

for (var i = 0; i < N; ++i) {
    var x = getUserResult(currentState);
    updateState(currentState, x);
}

How can I execute that kind of code in a browser and/or Node.js, without any security risks?

More generally, how can I execute a Javascript function that is not allowed to modify or even read the current webpage or any other global state? Is there something like an in-browser "JS virtual machine"?

How does JSFiddle ensure that you cannot run any malicious code (at the very least it could phish your login name, run a bot for the lifetime of the page, if not do much worse things)? Or doesn't it ensure that at all?


Solution

  • After much consideration and with the help of other posters in this thread (thank you so much for your help!), I found a first bunch of answers to my questions. I am re-writing my answer here though, because it summarizes the concepts and also gives you some actual code to experiment with.

    Generally, there are two solutions to this problem: We can either use iframe or Worker to run code in an isolated environment, thus making it impossible to read or write the current page's information (which is my first major security concern).

    Caja + Closure

    There are more complete sandbox solutions such as Google Caja, which (by default) also runs its code in an iframe. Caja does not only sandbox JS, but also HTML and CSS. However, it does not seem very actively maintained anymore.

    Update: Caja has been deprecated since the original posting. It has been replaced with the Closure Toolkit, specifically Closure Library and Closure Templates.

    WebWorker + Blacklisting

    As proposed by Juan Garcia, I am going with the web worker API, but that is not the complete story. Even though, the worker cannot directly access anything from the hosting page, there are still quite a few security risks. This site lists all built-ins available in a Worker's context.

    This JSFiddle demonstrates a way to run a string of code outside the window's context without having to go through the server, which is still unsafe, as pointed out in the comments.

    I have extended on that and employed a black-list based approach to disable all outside communication by taking away all of the following:

    • Worker
    • WebSocket
    • XMLHttpRequest
    • importScripts

    Webworker + Whitelisting

    The actually safest approach however, is a white-list approach, (mentioned here), as it will stay safe into the future (given that the semantics of any whitelisted globals will never change in such a way that they allow more access in any future release; which is a somewhat reasonable assumption).

    I ended up implementing the whitelist approach in my in-browser WumpusGame that allows you to write your own AI script while playing the game, a few years back. Source code here. You can see that the Whitelist is maintained in GuestScriptContext while the workers are managed by the HostScriptContext.

    The GameScriptContext shows how you can use it.

    It has several features, including guest<->host communication, guest can execute non-privileged actions (and if it has a security token even privileged actions) on the host, and a lot more.

    NOTE: It does not play well with some extensions, as they will prevent overriding globals (when I try to run it, it complains about TEMPORARY being one of them). The fix would be to either whitelist those, or figure out how to make it play nice with modern browser security features, or just security-focused extensions, such as AdBlock etc.

    The game is theoretically playable here, but because of reasons above it does not seem to work in Chrome (and I haven't tried other browsers yet).

    Some more references: