Sandboxing Rhino in Java

January 2, 2009 – 12:07 am Tags: , ,

I’ve been working on a Java app which needed Rhino for scripting. The app would need to run untrusted JavaScript code from 3rd parties, so I had to find a way to block access to all Java methods, except the ones I wanted. This would not be a problem if there was an easy way to disable LiveConnect – the feature of Rhino which provides java access to scripts – but there is no such thing.

However, after a lot of digging around, I finally found a way to do this without too much hacking. In fact, it can be done by just extending a few of the Rhino classes, and using the setters provided to override some of the default ones.

ClassShutter

Let’s first look at the ClassShutter, which can be used to restrict access to Java packages and classes.

//cx is the Context instance you're using to run scripts
cx.setClassShutter(new ClassShutter() {
	public boolean visibleToScripts(String className) {					
		if(className.startsWith("adapter"))
			return true;
 
		return false;
	}
});

The above will effectively disable access to all Java classes onwards from the point where the shutter was set. However, if you run any scripts before setting the shutter, classes accessed there can still be used! You can use this to your advantage, for example to provide specific classes in the scripts under different names or such.

You probably noticed the comparison to “adapter” in the shutter. This is for when you implement interfaces or extend Java classes. Rhino will create new classes based on those interfaces/classes, and they will be called adapterN, where N is a number. If you block access to classes starting with adapter, you can’t implement or extend, and my use-case required that.

However, there is a limitation in the ClassShutter…

Reflection

As you may know, you can use someInstance.getClass().forName(“some.package.Class”).newInstance() to get a new instance of some.package.Class.

This will not get blocked by the ClassShutter! We need to disable access to getClass() to block this.

While the ClassShutter is relatively well documented, doing this required more research. A post in the Rhino mailing list finally pushed me to the right direction: Overriding certain NativeJavaObject methods and creating a custom ContextFactory and WrapFactory for that.

Here is an extended NativeJavaObject, which blocks access to getClass. You could use this approach to block access to other methods too:

public static class SandboxNativeJavaObject extends NativeJavaObject {
	public SandboxNativeJavaObject(Scriptable scope, Object javaObject, Class staticType) {
		super(scope, javaObject, staticType);
	}
 
	@Override
	public Object get(String name, Scriptable start) {
		if (name.equals("getClass")) {
			return NOT_FOUND;
		}
 
		return super.get(name, start);
	}
}

To make the above class work, you need two more classes:

A WrapFactory which returns our SandboxNativeJavaObject’s

public static class SandboxWrapFactory extends WrapFactory {
	@Override
	public Scriptable wrapAsJavaObject(Context cx, Scriptable scope, Object javaObject, Class staticType) {
		return new SandboxNativeJavaObject(scope, javaObject, staticType);
	}
}

And a ContextFactory, which returns Context’s which use SandboxWrapFactory:

public class SandboxContextFactory extends ContextFactory {
	@Override
	protected Context makeContext() {
		Context cx = super.makeContext();
		cx.setWrapFactory(new SandboxWrapFactory());
		return cx;
	}
}

Finally, to make all this work, we need to tell Rhino the global ContextFactory:

ContextFactory.initGlobal(new SandboxContextFactory());

With this, we are done. Now, when you use ContextFactory.getGlobal().enterContext(), you will get sandboxing contexts. But why did we need to set it globally? This is because it would appear that certain things, such as the adapter classes, use the global context factory to get some context for themselves, and without setting the global factory, they would get unlimited access.

In closing

I hope this is useful for someone. It took me a long time to figure it all out, so here it is now, all documented in one place. =)

The mailing list post where I found the direction for blocking getClass can be found here. Thanks to Charles Lowell.

There is also the SecurityController, which may be useful in further securing the class.

And as a final warning, while this approach works for me, and I haven’t yet found any way to get past the sandboxing and into Java-land… but there may be a way, and if you find one, do let me know.

Share this:

RSS feed Subscribe to my RSS feed

About the author

Jani is a 15 year veteran of the software industry. He's currently available for consulting

  1. 26 Responses to “Sandboxing Rhino in Java”

  2. dugg…
    http://digg.com/programming/Sandboxing_Rhino_in_Java

    Thanks for the info.

    By Thilo on Apr 20, 2009

  3. THANKS for sharing this amazing work.

    Sandboxing rhino should be available out of the box, why it’s not is totally beyond me. You just saved me a full day of ranting :-)

    By franck on Jun 17, 2009

  4. Thankyou very much!
    Saved my sanity! Can’t understand why turning off live connect is not a standard Rhino option – one of its biggest strengths is nice separation of Java function access – until it come to live connect!

    Have digged it too!

    By Jonathan Wilson on Jun 18, 2009

  5. This will be very useful. I am trying to do a similar thing. Do you know if using the Rhino provided by javax.script in jdk1.6 allows a comparable sandboxing control?

    By david on Jun 20, 2009

  6. I tried using the JDK Rhino, but it didn’t work for me. I don’t remember what the specific issue with it was – could have very well been this.

    By Jani Hartikainen on Jun 20, 2009

  7. doesn’t the getClass() get blocked by the shown ClassShutter because the ClassShutter blocks access to java.lang.Class?

    By david on Jun 22, 2009

  8. Many, many thanks for figuring this out. This helps me out a lot.

    I’ve also figured out a couple more details for it:

    Regarding david’s question on Jun 20, 2009, I’m able to use the rhino present in jdk-6u14, but I have to import sun.org.mozilla.javascript.internal.* instead of org.mozilla.javascript.*. Also, I have to include rt.jar from the jdk/jre/lib dir in the classpath explicitly both at compile and run time. This violates Sun’s rules about what you’re not supposed to see and use in the JDK/JRE ‘system’ libraries.

    But other than that, it works exactly as described above.

    However, using rhino1_7R2, I see the same thing david mentioned Jun 22, 2009.
    I used:

    i = java.lang.Integer.valueOf(“5″);
    sb=i.getClass().forName(“java.lang.StringBuffer”).newInstance();

    …and allowed java.lang.Integer in the ClassShutter.

    ClassShutter is called with “java.lang.Class” when getClass() is called and it’s blocked.

    When I allow java.lang.Class, ClassShutter then blocked java.lang.StringBuffer.

    Only if I allow also allow java.lang.StringBuffer explicitly does it work.

    It looks to me like the SandBox* classes for Reflection prevention are no longer necessary because the Reflection hole was closed somewhere along the way in Rhino. But that update hasn’t made it into the JDK/JRE yet.

    By Jimm on Jun 26, 2009

  9. How do get the Context instance with the JDK Rhino ?

    By Dahu on Sep 30, 2009

  10. Valuable research … thanks for sharing.

    By erlen on Feb 5, 2010

  11. This code will not prevent Rhino from exposing the class using the javascript:

    foo['class']

    Because it looks like a bean, it’s exposed as a property. Adding the following closes that particular hole:

    if ( name.equals(“class”) ) { return NOT_FOUND; }

    Sandboxing is much more than simply prohibiting access to a Class instance. Moreover, it would be best for properties/members/methods that are prohibited to not be visible at all.

    By DU on Jul 21, 2010

  12. Adding this override to the NativeJavaObject subclass will prevent unwanted members from being seen to exist:

    @Override
    public boolean has (String name, Scriptable start) {
    if ( !(name.equals(“exposed1″) || name.equals(“exposed2″)) ) return false;
    return super.has(name, start);
    }

    The details of how the list of exposed/forbidden names is managed is obviously flexible. This alone will not prevent a member or method from being accessed — it just makes it not show how when listing properties. So the get() override is also needed.

    BTB, the original posting is excellent and very helpful.

    By DU on Jul 21, 2010

  13. I have made a ClassShutter (or SandboxShutter) that allows you have fine grained access to every Java object, per field, per method, per instance.

    http://riven8192.blogspot.com/2010/07/java-rhino-fine-grained-classshutter.html

    By Riven on Jul 23, 2010

  14. DU, didn’t realize Rhino handled that in a specialized manner. Thanks for pointing it out.

    By Jani Hartikainen on Jul 25, 2010

  15. Actually, I wrote this myself, Rhino doesn’t support this fine grained access control out of the box.

    By Riven on Jul 25, 2010

  16. Hi
    Is there a way to restrict access to static methods?

    If i have the class :

    package com.xx;
    public class ClassA {

    public static void staticMethod(){
    // do domething
    }
    }

    how can i restrict js like:
    com.xx.ClassA.staticMethod();

    By NTony on Oct 26, 2010

  17. NTony, sorry but it’s been quite a while since I last touched this topic, so I would suggest posting your questions to, for example, Stack Overflow. You’ll probably have better luck there :)

    By Jani Hartikainen on Oct 26, 2010

  18. Sorry to resurrect an old discussion, but in all my research this excellent post seems to have become the definitive “go to” place for ClassShutter information.

    I’m also trying to get this to work under the JDK but like Dahu I can’t figure out where you get an instance of sun.org.mozilla.javascript.internal.Context in order to set the ClassShutter. It’s not for want of trying, and I reckon there will be others who are similarly puzzled.

    By Glenn Lawrence on Mar 2, 2011

  19. Here’s an update on what I have tried and where I am stuck. If nothing else it may help someone else from trying dead ends.
    1) I found you can get a context using static method sun.org.mozilla.javascript.internal.Context.enter()
    2) You can’t call setClassShutter() on a context returned by the above. It throws a security exception saying that you can’t replace it.
    3) I tried making a new ContextFactory to control the context creation and installing it using ContextFactory.initGlobal() but this causes javax.script.ScriptEngineManager.getEngineByName() to fail when you try to run the script

    So at this stage I still haven’t figured out how to get a Context where I can set my own ClassShutter

    By Glenn Lawrence on Mar 2, 2011

  20. Glenn – perhaps you are having trouble because you are using JDK6 bundled Rhino implementation rather than the Rhino stand-alone download.
    There is an excellent Rhino sandboxing implementation suggested here:
    http://riven8192.blogspot.com/2010/07/java-rhino-fine-grained-classshutter.html

    By Leo Haskin on Jun 11, 2011

  21. Thanks Leo. Yes I came to the same conclusion and gave up on using the bundled Rhino and added the Rhino library explicitly. It now works. Thanks!

    By Glenn Lawrence on Jun 13, 2011

  22. Thank you for the post. It was very helpful.

    By nicolae on Nov 24, 2011

  23. Awesome work, thanks a ton!

    By Jeb Beich on Jan 17, 2012

  24. Thanks for sharing! This is a great post, and very easy to understand. Seems to cover all bases.

    By Yuri Brigance on Mar 25, 2013

  25. This may be great work, but it’s from about 6 years ago. Is it still the case that THIS is the best way to sandbox Javascript? Has Rhino, or Oracle not done one thing in 6 years to fix this? I’ve been searching half the day on how to sandbox safely and absolutely nothing works, and then there’s Metasploit too. Anybody alive in 2013 who can set me straight ?

    By Clay Ferguson on Jul 10, 2013

  26. I’m interested in embeding rhino in GAE. This article shows really interesting example of rhino security management.

    By Pani Architekt on Sep 24, 2013

  1. 1 Trackback(s)

  2. Aug 4, 2009: JCN Blog » Blog Archive » Apache Felix administration with JavaScript

Post a Comment

You can use some HTML (a, em, strong, etc.). If you want to post code, use <pre lang="PHP">code here</pre> (you can replace PHP with the language you are posting)