Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Secure Nashorn JS Execution

How can I securely execute some user supplied JS code using Java8 Nashorn?

The script extends some computations for some servlet based reports. The app has many different (untrusted) users. The scripts should only be able to access a Java Object and those returned by the defined members. By default the scripts could instantiate any class using Class.forName() (using .getClass() of my supplied object). Is there any way to prohibit access to any java class not explicitly specified by me?

like image 629
tom_ma Avatar asked Dec 27 '13 00:12

tom_ma


People also ask

Why Nashorn is deprecated?

With the release of Java 11, Nashorn was deprecated citing challenges to maintenance, and has been removed from JDK 15 onwards. Nashorn development continues on GitHub as a standalone OpenJDK project and the separate release can be used in Java project from Java 11 and up.

Is Nashorn deprecated?

The Nashorn engine has been deprecated in JDK 11 as part of JEP 335 and and has been removed from JDK15 as part of JEP 372. GraalVM can step in as a replacement for JavaScript code previously executed on the Nashorn engine. GraalVM provides all the features for JavaScript previously provided by Nashorn.

Does Nashorn support ES6?

The current implementation of Nashorn conforms to the ECMAScript 5.1 specification with the addition of new ECMAScript 6 (ES6) features in subsequent JDK releases under JEP 292: Implement Selected ECMAScript 6 Features in Nashorn.


2 Answers

I asked this question on the Nashorn mailing list a while back:

Are there any recommendations for the best way to restrict the classes that Nashorn scripts can create to a whitelist? Or is the approach the same as any JSR223 engine (custom classloader on the ScriptEngineManager constructor)?

And got this answer from one of the Nashorn devs:

Hi,

  • Nashorn already filters classes - only public classes of non-sensitive packages (packages listed in package.access security property aka 'sensitive'). Package access check is done from a no-permissions context. i.e., whatever package that can be accessed from a no-permissions class are only allowed.

  • Nashorn filters Java reflective and jsr292 access - unless script has RuntimePermission("nashorn.JavaReflection"), the script wont be able to do reflection.

  • The above two require running with SecurityManager enabled. Under no security manager, the above filtering won't apply.

  • You could remove global Java.type function and Packages object (+ com,edu,java,javafx,javax,org,JavaImporter) in global scope and/or replace those with whatever filtering functions that you implement. Because, these are the only entry points to Java access from script, customizing these functions => filtering Java access from scripts.

  • There is an undocumented option (right now used only to run test262 tests) "--no-java" of nashorn shell that does the above for you. i.e., Nashorn won't initialize Java hooks in global scope.

  • JSR223 does not provide any standards based hook to pass a custom class loader. This may have to be addressed in a (possible) future update of jsr223.

Hope this helps,

-Sundar

like image 88
Kong Avatar answered Sep 23 '22 10:09

Kong


Added in 1.8u40, you can use the ClassFilter to restrict what classes the engine can use.

Here is an example from the Oracle documentation:

import javax.script.ScriptEngine; import jdk.nashorn.api.scripting.ClassFilter; import jdk.nashorn.api.scripting.NashornScriptEngineFactory;   public class MyClassFilterTest {     class MyCF implements ClassFilter {     @Override     public boolean exposeToScripts(String s) {       if (s.compareTo("java.io.File") == 0) return false;       return true;     }   }     public void testClassFilter() {       final String script =       "print(java.lang.System.getProperty(\"java.home\"));" +       "print(\"Create file variable\");" +       "var File = Java.type(\"java.io.File\");";       NashornScriptEngineFactory factory = new NashornScriptEngineFactory();       ScriptEngine engine = factory.getScriptEngine(       new MyClassFilterTest.MyCF());     try {       engine.eval(script);     } catch (Exception e) {       System.out.println("Exception caught: " + e.toString());     }   }     public static void main(String[] args) {     MyClassFilterTest myApp = new MyClassFilterTest();     myApp.testClassFilter();   } } 

This example prints the following:

C:\Java\jre8 Create file variable Exception caught: java.lang.RuntimeException: java.lang.ClassNotFoundException: java.io.File 
like image 24
mkobit Avatar answered Sep 23 '22 10:09

mkobit