问题
I'm implementing a sandboxed JS environment that allows users to upload their JS code and trigger it based on a set of rules.
I disabled Java access from Nashorn environment and only allow accessing some utility classes for a few operations such as HTTP requests, base64 encoding etc.
Currently, I create ScriptEngine
(Nashorn environment) for each JS code uploaded by users but in our environment we have many pre-defined JS code which are used by most of our users. Since creating ScriptEngine
is expensive, I want to re-use ScriptEngine
s for same JS code blocks. Let's say that the user uploaded the following code:
var main = function(event, userContext) {
return event.get('time') + userContext.api_key;
}
userContext
works similar to environment variables, the user sets context variables when uploading her code and we pass them to the main
function. Since the JS code doesn't have any variable other than main, it's stateless so re-using the container is easy. However for the following code block, it's not trivial to re-use the container:
var test = [];
var main = function(event, userContext) {
test.push(1);
return event.get('time') + userContext.api_key;
}
I'm looking for a way to abstract the local variables for each user and re-use the environment with different local context so that Java will compile the JS code to Java bytecode and once and re-use the same bytecode and invoke it in a different context, just like the relation between classes and objects.
One way to do it is to create a ENGINE_SCOPE
for each user and change bindings when invoking the main function. However I couldn't find a way to clone ScriptObjectMirror
. My plan was to create new scope when a user uploads a pre-compiled JS code and use it when invoking the function. However Nashorn doesn't seem to allow creating engine scope programatically.
Another problem is that JS allows modifying global objects. The user can execute the following code block without any problem and Object
will be null
for all executions:
var test = [];
var main = function(event, userContext) {
Object = null;
return event.get('time') + userContext.api_key;
}
Therefore, I also need to disable modifying the variables in GLOBAL_SCOPE
but I couldn't find a way to do it.
I'm aware that there might be other security problems for re-using containers and the safe way is to create different containers for each JS code uploaded by users. However creating Nashorn environment is quite expensive compared to V8 and if there are too many Nashorn environments in runtime, Java fills up the Code Cache
since it tries to compile the JS code blocks to Java bytecode. I'm also open to other suggestions to solve this problem.
回答1:
I believe ScriptContext
objects will isolate each execution.
ScriptEngine engine = new ScriptEngineManager()
.getEngineByName("nashorn");
// Set up an isolated context.
Bindings bindings = engine.createBindings();
ScriptContext isolatedContext = new SimpleScriptContext();
isolatedContext.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
// Compile the function in the context.
engine.eval(code, isolatedContext);
ScriptObjectMirror fn = (ScriptObjectMirror) isolatedContext
.getAttribute("main");
// Call it whenever
fn.call("main", arg1, ar2, argEtc);
来源:https://stackoverflow.com/questions/40828426/safely-re-using-sandboxed-nashorn-containers