I need to run untrusted server-side code in a web app - what are my options?
Here are a few ways you can run untrusted code:
- a docker container that runs the code, I would suggest checking codecube.io out, it does exactly what you want and you can learn more about the process here
- using the libsandbox libraries but at the present time the documentation is pretty bad
- PyPy’s sandboxing
JavaScript eval() on client-side for running untrusted code
If you have no path in your logic that allows one person to publish code to be used by others then you can use eval()
as it is.
Your situation is not anyhow different from any user that has browser and dev tools in it where he/she can run any code they want.
Run untrusted code at Google Cloud Functions
If I understand your request correctly, you are looking to have Cloud HTTP Functions evaluate user-provided Javascript code on the server side.
By your description, the only real ways the function would be able to evaluate the user's code would be essentially using eval
or new Function()
. To confirm the risks I mentioned, I created a cloud function that simply passes the POST request body to eval
. Without any dependencies, I could issue HTTP requests on behalf of the cloud function which could be quite bad.
Given that most useful cloud functions would have "@google-cloud"
as a dependency, the user could gain access to that context. I was able to require @google-cloud
and get all the information accessible to that object (application credentials, application information, etc.). Having such information available to a malicious user is considerably worse that the first test. In addition, Cloud Functions are authenticated by default, presumably by default application credentials, thus gaining all the abilities of the gcloud client library.
In the end, the safest way to run user-provided code on the server would be within a container. This would essentially lock the user's code into a Linux box where the resources and networking capabilities can be entirely governed by you. On the Google Cloud Platform, you're best means of accomplishing this would likely be using App Engine as a front-end to handle user requests and Compute Engine VMs to create and run containers for user code. It's more complex but doesn't risk destroying your Google Cloud Platform project.
Prevent system calls in Node.js when running untrusted user code
You are looking for the runInNewContext
function from the vm
module (vm documentation).
When you use this function it creates a VERY limited context. You'll need to pass anything you want into the sandbox object which become global objects. For example: You will need to include console
in the sandbox object if you want your untrusted code to write to the console.
Another thing to consider: Creating a new context is a VERY expensive operation - takes extra time and memory to do. Seriously consider if you absolutely need this. Also seriously consider how often this is going to happen.
Example:
var vm = require('vm');
var sandbox = {
console: console,
msg: "this is a test",
};
vm.runInNewContext('console.log(msg);', sandbox, 'myfile.vm');
// this is a test
More to consider: You will want to create a new process to run this in. Even though it's in a new context it's still in the same process that it's being called from. So a malicious user could simply set a never ending for
loop so that it never exits. You'll need to figure out logic to know when something like this happens so that you can kill the process and create a new one.
Last thought: A new context does not have setTimeout
or setInterval
. You may or may not want to add these. However, if you create a setInterval
in the untrusted code and the untrusted code never stops it then it will continue on forever. You'll need to figure a way to end the script, it's probably possible I just haven't looked into it.
How to run user-submitted scripts securely in a node.js sandbox?
You should always run untrusted code in a separate process, which is exactly what the sandbox module does. A simple reason is that vm.runInNewContext('while(true){}', {})
will freeze node.
It starts by spawning a separate process, which will later send the result serialized to JSON on its stdout. The parent process continues executing regardless of what the child does and can trigger a timeout.
The untrusted code is then wrapped in a closure with strict mode (in regular JavaScript, you can use arguments.callee.caller
to access data outside of your scope). Finally, a very limited global
object is passed to prevent access to node's API. The untrusted code can only do basic computation and has no access to files or sockets.
While you should read sandbox's code as an inspiration, I wouldn't recommend using it as is:
- The code is getting old and hasn't been updated for 7 months.
- The Child Process module in node already provides most of the features you need, especially child_process.fork().
- The IPC channel provided by child_process.fork probably has better performances.
For increased security, you could also consider using setuid-sandbox. It's the code used by Google Chrome to prevent tab processes from accessing the file system. You would have to make a native module, but this example seems straightforward.
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')
.
Related Topics
Does Schema.Org Markup Work If Markup Is Dynamically Built With JavaScript
How to Refer to JavaScript Variables Across Webpages in a Browser Session
Want to Add "Addeventlistener" on Multiple Elements With Same Class
Positioning ≪Div≫ Element At Center of Screen
Creating Svg Elements Dynamically With JavaScript Inside Html
PHP+Js: How to Do Fileuploads in HTML Form as Content-Type Multipart (Via Js)
How to Make an HTML Text Box Show a Hint When Empty
How to Connect HTML Divs With Lines
Smooth Scroll Without the Use of Jquery
Call JavaScript Function After Script Is Loaded
How to Get the Onclick Calling Object
How to Make a Jquery "$.Post" Request Synchronous
Conditionally Load JavaScript File
Is There a Dom Event That Fires When an HTML Select Element Is Closed