Jython运行不受信任的Python代码需要哪些安全特权?

7

简介

Jython在JVM中运行并带有安全管理器。 安全管理器不应阻止Jython引擎本身完成其工作,但不应允许在Jython中运行的Python脚本执行任何特权操作。 到底使用什么最小的Java安全策略可以实现这一点?

换句话说(完整版)

本问题涉及使用Java的安全机制来对不受信任的Python脚本进行隔离。(我不想讨论其他隔离Python的方法)。 如果这种安全方法存在根本缺陷,请说明。

不受信任的Python代码将调用可信Java对象上的方法,并嵌入到Java中作为PyObject。 这在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中哪些类需要授权,以及它们需要什么特定的权限?
如果这种方法注定要失败,那么一个这样的答案将是很好的。

我想你已经意识到,任何你授权的操作都将允许 Python 代码执行该操作 - 也就是说,你给 Jython 的每个权限,都会赋予它运行的代码。 - Marcin
@Marcin:如果这是严格的真实情况,那么我认为这种方法注定要失败。当然,如果我们一次性将权限授予整个Jython库,那么你是正确的。但我希望将权限授予Jython中的单个类,以便只有这些Jython类可以执行特权操作。 - Travis Wilson
可能是Java文件权限的线程问题的重复。 - Travis Wilson
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 jar文件中移动到单独的jar文件中,以授予Jython解释器此权限,而不必授予在解释器中运行的Python代码。

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

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

值得注意的是,从Python代码中很难利用createClassLoader权限。我能够在Python脚本内部执行此操作,但我必须扩展java.secure.SecureClassLoader,只有在授予accessDeclaredMembers权限的情况下才可能实现。根据您对风险的接受程度,如果您不授予accessDeclaredMembers权限,可能不值得去改变Jython jar文件以阻止createClassLoader权限。

还值得指出的是,createClassLoader修复非常容易受到Jython内部变化的影响,我只测试了Jython 2.5.3。

我在博客文章中详细介绍了这个问题。


0

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


链接已经失效了。你能提供一个替代的链接吗? - Jochen Bedersdorfer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接