How to Sandbox JavaScript Running in the Browser

How to run a JavaScript function in a sandbox environment?

Sandbox is a node module that according to the README;

  • Can be used to execute untrusted code
  • Support for timeouts (e.g. prevent infinite loops)
  • Restricted code (cannot access node.js methods)

The Halting Problem as @maerics wrote about can be solved by setting a timeout for the code although you can not do that in the same process, because for example while(1) will block it. Sandbox addresses this issue by using a child process.

The variable problem should therefore also be solved because Sandbox is in a child process and not the main process.

As mentioned before, if possible, you should avoid users to run arbitrary code on your server because it comes with an huge security risk. Even through the module provides this restrictions you should run at least the child processes with an as unprivileged user as possible.

Is there any way to sandbox some javascript so that it can not in any way send data to a server

You can't cut off any native transport for script.

For example, Worker has access only to one native transport XMLHttpRequest(because no access to document -> no node with src, link, forms ) and you can redefine it ie window.XMLHttpRequest = function () {return 1} and script can't send data to server.

But just run delete window.XMLHttpRequest and you will set back native XMLHttpRequest. It works fine and in strict mode (ECMAScript-262 ed. 5/6)

(function () {

'use strict';

window.XMLHttpRequest = function () {return 1};

console.log(window.XMLHttpRequest);

delete window.XMLHttpRequest;

console.log(window.XMLHttpRequest);

})()

About HTML5 iframe options. If you use sandbox="allow-scripts allow-same-origin allow-pointer-lock" any script from iframe can't send cross-domain requests(XMLHttpRequest,postMessage,WebSocket, WebRTC, Server-Sent Events...any). If you need to denied cross-domain request - that is it.

Safely sandbox and execute user submitted JavaScript?

This answer is outdated as gf3 does not provide protection against sandbox breaking

http://gf3.github.io/sandbox/ - it uses require('child_process') instead of require('vm').

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

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:

  • The W3Schools web worker tutorial.
  • This thread and this html5rocks tutorial on how to run a Worker without separate files.


Related Topics



Leave a reply



Submit