7

简单来说

Jython 在带有安全管理器的 JVM 中运行。安全管理器不应阻止 Jython 引擎本身完成其工作,但不应允许在 Jython 中运行的 python 脚本执行任何特权操作。实现此目的的最低 Java 安全策略是什么?

换句话说(长版)

这个问题是关于使用 Java 的安全机制对不受信任的 python 脚本进行沙箱处理。(我不想讨论沙箱 python 的其他方法。)如果这种安全方法存在根本问题,请说出来。

不受信任的 python 代码将调用受信任的 Java 对象上的方法,并将作为 PyObject 嵌入到 Java 中。这在Jythonbook 的示例中进行了解释,我们将使用该示例中的代码。这是 Java 1.6 上的 Jython 2.5.2。

我们需要向安全管理器提供一些非常具体的指令,因为 Jython 自己执行一些特权操作,但它也执行不应该具有任何这些特权的不受信任的 python 代码。

首先,为了安全,我们在 Java VM 中安装了一个 SecurityManager:

/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

伟大的。但是对我们来说,Building.py 是不受信任的代码,所以我们锁定了 JVM。问题是,这削弱了 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}

安全管理器不允许任何人读取“user.dir”属性。当然,Jython 本身读取该属性应该没问题。我可以将其写入这样的策略文件吗?

/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

好吧,不,我不能……因为不受信任的 python 代码也在 python 解释器类中运行。所以不受信任的 python 也会获得这个特权,这很糟糕。

所以我只需要授权一个特定的 jython 类,在本例中为 PySystemState。(我可能还需要编辑它的源代码,以便它在doPrivilegedAction调用中运行某些代码。)

伟大的。下一个必需的权限是一个非常危险的权限:危险的 createClassLoader 权限。

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)

依偎在堆栈的底部,我们看到PythonInterpreter.exec触发了这个访问请求。是否exec()必须作为 PrivilegedAction 运行?

所以这个发现过程可能会持续一段时间,我希望有人能简单地知道答案:Jython 中的哪些类需要授权,它们需要哪些特定权限?

而且,如果这种方法注定要失败,那么对这种效果的回答将是很好的。

4

2 回答 2

6

对于 Jython 2.5.3(我一直在使用的版本),最低权限是:

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";

如果您希望脚本能够继承 Java API 类,您还需要:

permission java.lang.RuntimePermission "accessDeclaredMembers";

至于createClassLoader权限,这可能是个问题,但我发现您可以将此权限授予 Jython 解释器,而无需将其授予在解释器中运行的 Python 代码,方法是将以下类从 Jython jar 移动到单独的 jar 中。

org.python.core.PyReflectedConstructor
org.python.core.PyReflectedField
org.python.core.PyReflectedFunction

这个单独的 jar 放置在标准 Jython jar 旁边的类路径中,但在 Java 安全策略文件中没有被授予任何权限,因此这些类在脚本尝试调用 Java API 时始终位于调用堆栈上,因此被阻止从利用createClassLoader许可。

还值得注意的是,利用createClassLoaderPython 代码的权限并不容易。我可以在 Python 脚本中执行此操作,但我必须扩展java.secure.SecureClassLoader,这只有在accessDeclaredMembers也获得许可的情况下才有可能。根据您对风险的偏好,可能不值得为了阻止而弄乱 Jython 罐子createClassLoader,尤其是在您不同意的情况下accessDeclaredMembers

还值得指出的是,该createClassLoader修复非常容易受到 Jython 中的更改的影响,而且我只使用 Jython 2.5.3 对其进行了测试。

我在博客文章中对此进行了更详细的描述。

于 2014-08-03T00:54:47.737 回答
0

看看 java 沙盒项目 ( http://blog.datenwerke.net/p/the-java-sandbox.html )。它允许您灵活地设置安全管理器,并在允许哪些类做什么方面更具体。然而,真正保护任意用户代码是困难的,特别是如果所讨论的语言实现(在您的情况下为 jython)不尝试使用最小权限(例如,期望使用反射、类加载器等)。

于 2013-08-29T20:55:01.947 回答