Domi Domi - 6 months ago 41
Javascript Question

How to safely run user-supplied Javascript code?

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?

Answer

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). In addition, there are sandbox approaches such as Google Caja, but that (by default) also runs its code in an iframe.

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

However, I am currently working on porting it to a white-list approach, as explained here, to also make it safe for the (near) future.

For more information, please consider:

Comments