Jython is running in a JVM with a security manager. The security manager should not prevent the Jython engine itself from doing its job, but it should not allow any privileged action by the python script running within Jython. What is the minimum Java security policy that will accomplish this?
This question is about using Java's security mechanisms to sandbox untrusted python scripts. (I don't want a discussion about other ways to sandbox python.) If there's something fundamentally wrong with this approach to security, please say so.
The untrusted python code will invoke methods on trusted Java objects, and will be embedded in Java as a PyObject. This is explained in Jythonbook's example, and we'll use the code from that example. This is Jython 2.5.2 on Java 1.6.
We need to give some very specific instructions to the security manager, because Jython does some privileged actions of its own, but it also executes untrusted python code that should not have any of those privileges.
First, for security, we install a SecurityManager in the Java VM:
/home/me% export CLASSPATH=.:jython.jar
/home/me% javac ./org/jython/book/interfaces/BuildingType.java ./org/jython/book/Main.java ./org/jython/book/util/BuildingFactory.java
/home/me% java org.jython.book.Main
Building Info: null BUILDING-A 100 WEST MAIN
Building Info: null BUILDING-B 110 WEST MAIN
Building Info: null BUILDING-C 120 WEST MAIN
Great. But for us, Building.py is untrusted code so we lock down the JVM. Problem is, that cripples Jython:
/home/me% java -Djava.security.manager org.jython.book.Main
Jul 23, 2013 7:07:17 PM org.python.google.common.base.internal.Finalizer getInheritableThreadLocalsField
INFO: Couldn't access Thread.inheritableThreadLocals. Reference finalizer threads will inherit thread local values.
Exception in thread "main" java.security.AccessControlException: access denied (java.util.PropertyPermission user.dir read)
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:323)
at java.security.AccessController.checkPermission(AccessController.java:546)
at java.lang.SecurityManager.checkPermission(SecurityManager.java:532)
...
at java.io.File.getAbsolutePath(File.java:501)
at org.python.core.PySystemState.<init>(PySystemState.java:181)
at org.python.core.PySystemState.doInitialize(PySystemState.java:890)
at org.python.core.PySystemState.initialize(PySystemState.java:800)
at org.python.core.PySystemState.initialize(PySystemState.java:750)
at org.python.core.PySystemState.initialize(PySystemState.java:743)
at org.python.core.PySystemState.initialize(PySystemState.java:737)
at org.python.core.PySystemState.initialize(PySystemState.java:733)
at org.python.core.ThreadStateMapping.getThreadState(ThreadStateMapping.java:17)
at org.python.core.Py.getThreadState(Py.java:1315)
at org.python.core.Py.getThreadState(Py.java:1311)
at org.python.core.Py.getSystemState(Py.java:1331)
at org.python.util.PythonInterpreter.<init>(PythonInterpreter.java:102)
at org.python.util.PythonInterpreter.<init>(PythonInterpreter.java:92)
at org.python.util.PythonInterpreter.<init>(PythonInterpreter.java:64)
at org.jython.book.util.BuildingFactory.<init>(BuildingFactory.java:22)
at org.jython.book.Main.main(Main.java:21){code}
Security Manager is not letting anyone read the "user.dir" property. Of course, it should be okay for Jython itself to read that property. Can I write that into a policy file like this?
/home/me% cat jython.policy
grant codeBase "/home/me/*" {
permission java.util.PropertyPermission "user.dir", "read";
};
/home/me% java -Djava.security.manager -Djava.security.policy=jython.policy org.jython.book.Main
Well, no, I can't ... because the untrusted python code also runs within the python interpreter classes. So untrusted python would also get this privilege, and that's bad.
So I need to authorize only a specific jython class, in this case PySystemState. (I may also need to edit its source code so that it runs certain code within a doPrivilegedAction
call.)
Great. The next required privilege is a doozy: the dangerous createClassLoader permission.
java.security.AccessControlException: access denied (java.lang.RuntimePermission createClassLoader)
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:323)
at java.security.AccessController.checkPermission(AccessController.java:546)
at java.lang.SecurityManager.checkPermission(SecurityManager.java:532)
at java.lang.SecurityManager.checkCreateClassLoader(SecurityManager.java:594)
at java.lang.ClassLoader.<init>(ClassLoader.java:226)
at java.security.SecureClassLoader.<init>(SecureClassLoader.java:76)
at java.net.URLClassLoader.<init>(URLClassLoader.java:113)
at org.python.core.BytecodeLoader$Loader.<init>(BytecodeLoader.java:81)
at org.python.core.BytecodeLoader.makeClass(BytecodeLoader.java:27)
at org.python.core.BytecodeLoader.makeCode(BytecodeLoader.java:67)
at org.python.compiler.LegacyCompiler$LazyLegacyBundle.loadCode(LegacyCompiler.java:43)
at org.python.core.CompilerFacade.compile(CompilerFacade.java:34)
at org.python.core.Py.compile_flags(Py.java:1703)
at org.python.core.Py.compile_flags(Py.java:1708)
at org.python.core.Py.compile_flags(Py.java:1738)
at org.python.util.PythonInterpreter.exec(PythonInterpreter.java:206)
at org.jython.book.util.BuildingFactory.<init>(BuildingFactory.java:23)
at org.jython.book.Main.main(Main.java:22)
Nestled towards the bottom of the stack we see that PythonInterpreter.exec
has triggered this access request. Does exec()
have to run as a PrivilegedAction?
So this discovery process could go on for a while, and I was hoping that someone would simply know the answer: What classes in Jython need to be authorized, and what specific permissions do they need?
And, if this approach is destined to fail, then an answer to that effect would be great.
The Jython project provides implementations of Python in Java, providing to Python the benefits of running on the JVM and access to classes written in Java. The current release (a Jython 2.7. x) only supports Python 2 (sorry).
Jython is a version of the Python programming language that runs on the Java platform. It allows users to write programs in Python and compile them to Java bytecodes that run directly on a Java Virtual Machine, or JVM. It's similar to otherJVM languages like; Scala, Kotlin, Groovy, or Clojur.
Jython is an open source implementation of the Python programming language, integrated with the Java platform. A programmer can compile Python source code to Java bytecode , and run the code on any Java virtual machine . The integration of Python and Java gives programmers access to all Java libraries.
For Jython 2.5.3 (the version I've been using), the minimum permissions are:
permission java.lang.RuntimePermission "createClassLoader";
permission java.lang.RuntimePermission "getProtectionDomain";
permission java.io.FilePermission "${user.dir}/*", "read";
permission java.util.PropertyPermission "java.vm.name", "read";
permission java.util.PropertyPermission "java.vm.vendor", "read";
permission java.util.PropertyPermission "os.name", "read";
permission java.util.PropertyPermission "os.arch", "read";
permission java.util.PropertyPermission "user.dir", "read";
permission java.util.PropertyPermission "line.separator", "read";
If you want scripts to be able to subclass Java API classes, you'll also need:
permission java.lang.RuntimePermission "accessDeclaredMembers";
As for the createClassLoader
permission, that could be a problem, but I've discovered that you can grant this permission to the Jython interpreter without granting it to Python code running in the interpreter by moving the following classes from the Jython jar into a separate jar.
org.python.core.PyReflectedConstructor
org.python.core.PyReflectedField
org.python.core.PyReflectedFunction
This separate jar is placed on the classpath alongside the standard Jython jar, but isn't granted any permissions in the Java security policy file, and so these classes, which are always on the call stack when scripts try to call Java APIs, are prevented from taking advantage of the createClassLoader
permission.
It's also worth noting that it's not easy to exploit the createClassLoader
permission from Python code. I was able to do from within a Python script, but I had to extend java.secure.SecureClassLoader
, which is only possible if the accessDeclaredMembers
permission is also granted. Depending on your appetite for risk, it might not be worth messing with the Jython jars just to block createClassLoader
, especially if you're not granting accessDeclaredMembers
.
It's also well worth pointing out that the createClassLoader
fix is very susceptible to changes within Jython, and I've only tested it with Jython 2.5.3.
I've written this up in more detail in a blog post.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With