I want to create an HTML+JS environment where user can enter and run arbitrary JavaScript code which will be executed in context of given jail object. I've set up a playground to illustrate what I have so far.
This one does a somewhat decent job:
2 + 2
4
this
returns jail object
this
[object Object]
this.hello()
runs expected method
this.hello()
hello world!
this.foo = function() { return 42; }
function () { return 42; }
this.foo()
42
hidden
ReferenceError: hidden is not defined
However, it completely fails to hide globally accessible properties, such as window
or document
for user:
window
[object Window]
ReferenceError: window is not defined
The best solution I've came up so far is to just fill up all the global variable I can think of with undefined
or null
right in the Jail object declaration, as illustrated in updated version. This way they seem to be lost forever inside the scope of jail and user won't be able to access them. My questions:
If it’s client-side and you can guarantee a modern browser, use web workers instead; they’re much safer, and you can also stop infinite loops from tying up the main thread, and implement timeouts by calling Worker#terminate
.
Start up a new worker for each execution:
var worker = new Worker('path/to/evaluator.js');
Receive messages from the worker:
worker.onmessage = function (e) {
console.log(e.data);
};
Send over the code to execute:
worker.postMessage(someCode);
In the worker, listen:
onmessage = function (e) {
postMessage(eval(e.data));
};
And make sure to call terminate
after receiving the message, too, because the worker can call postMessage
itself. (You can prevent that, but there’s really no point.)
Web workers don’t have access to anything in the main execution context, they run on another thread, and they’re implemented by the browser, so they’re much safer than the typical delete-dangerous-things sandbox.
They do, however, have access to XMLHttpRequest
; see Is It Possible to Sandbox JavaScript Running In the Browser? for other approaches if this is a problem.