如何创建 Java 沙箱?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1715036/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I create a Java sandbox?
提问by corgrath
I want to make my application to run other people's code, aka plugins. However, what options do I have to make this secure so they don't write malicious code. How do I control what they can or can not do?
我想让我的应用程序运行其他人的代码,也就是插件。但是,我有什么选择可以确保安全,这样他们就不会编写恶意代码。我如何控制他们能做什么或不能做什么?
I have stumbled around that JVM has a "built in sandbox" feature - what is it and is this the only way? Are there third-party Java libraries for making a sandbox?
我偶然发现 JVM 具有“内置沙箱”功能 - 它是什么,这是唯一的方法吗?是否有用于制作沙箱的第三方 Java 库?
What options do I have? Links to guides and examples is appreciated!
我有哪些选择?指南和示例的链接表示赞赏!
采纳答案by tangens
You are looking for a security manager. You can restrict the permissions of an application by specifying a policy.
回答by Dafydd Rees
Defining and registering your own security managerwill allow you to limit what the code does - see oracle documentation for SecurityManager.
Also, consider creating a separate mechanism for loading the code - i.e. you could write or instantiate another Classloaderto load the code from a special place. You might have a convention for loading the code - for example from a special directory or from a specially formatted zip file (as WAR files and JAR files). If you're writing a classloader it puts you in the position of having to do work to get the code loaded. This means that if you see something (or some dependency) you want to reject you can simply fail to load the code. http://java.sun.com/javase/6/docs/api/java/lang/ClassLoader.html
定义并注册自己的安全管理将允许你限制的代码做什么-请参见Oracle文档安全管理器。
另外,考虑创建一个单独的机制来加载代码——也就是说,你可以编写或实例化另一个类加载器来从一个特殊的地方加载代码。您可能有加载代码的约定——例如从特殊目录或特殊格式的 zip 文件(如 WAR 文件和 JAR 文件)。如果你正在编写一个类加载器,它会让你不得不做一些工作来加载代码。这意味着,如果您看到想要拒绝的某些内容(或某些依赖项),您可能无法加载代码。http://java.sun.com/javase/6/docs/api/java/lang/ClassLoader.html
回答by Tom Hawtin - tackline
For an AWT/Swing application you need to use non-standard AppContext
class, which could change at any time. So, to be effective you would need to start another process to run plug-in code, and deal with communication between the two (a little like Chrome). The plug-in process will need a SecurityManager
set and a ClassLoader
to both isolate the plug-in code and apply an appropriate ProtectionDomain
to plug-in classes.
对于 AWT/Swing 应用程序,您需要使用非标准AppContext
类,该类可以随时更改。因此,为了有效,您需要启动另一个进程来运行插件代码,并处理两者之间的通信(有点像 Chrome)。插件过程将需要一个SecurityManager
set 和 aClassLoader
来隔离插件代码并将适当的应用ProtectionDomain
到插件类中。
回答by Arno Mittelbach
Have a look at the java-sandbox projectwhich allows to easily create very flexible sandboxes to run untrusted code.
查看java-sandbox 项目,它允许轻松创建非常灵活的沙箱来运行不受信任的代码。
回答by Arno Unkrig
Here's how the problem can be solved with a SecurityManager:
以下是使用 SecurityManager 解决问题的方法:
package de.unkrig.commons.lang.security;
import java.security.AccessControlContext;
import java.security.Permission;
import java.security.Permissions;
import java.security.ProtectionDomain;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import de.unkrig.commons.nullanalysis.Nullable;
/**
* This class establishes a security manager that confines the permissions for code executed through specific classes,
* which may be specified by class, class name and/or class loader.
* <p>
* To 'execute through a class' means that the execution stack includes the class. E.g., if a method of class {@code A}
* invokes a method of class {@code B}, which then invokes a method of class {@code C}, and all three classes were
* previously {@link #confine(Class, Permissions) confined}, then for all actions that are executed by class {@code C}
* the <i>intersection</i> of the three {@link Permissions} apply.
* <p>
* Once the permissions for a class, class name or class loader are confined, they cannot be changed; this prevents any
* attempts (e.g. of the confined class itself) to release the confinement.
* <p>
* Code example:
* <pre>
* Runnable unprivileged = new Runnable() {
* public void run() {
* System.getProperty("user.dir");
* }
* };
*
* // Run without confinement.
* unprivileged.run(); // Works fine.
*
* // Set the most strict permissions.
* Sandbox.confine(unprivileged.getClass(), new Permissions());
* unprivileged.run(); // Throws a SecurityException.
*
* // Attempt to change the permissions.
* {
* Permissions permissions = new Permissions();
* permissions.add(new AllPermission());
* Sandbox.confine(unprivileged.getClass(), permissions); // Throws a SecurityException.
* }
* unprivileged.run();
* </pre>
*/
public final
class Sandbox {
private Sandbox() {}
private static final Map<Class<?>, AccessControlContext>
CHECKED_CLASSES = Collections.synchronizedMap(new WeakHashMap<Class<?>, AccessControlContext>());
private static final Map<String, AccessControlContext>
CHECKED_CLASS_NAMES = Collections.synchronizedMap(new HashMap<String, AccessControlContext>());
private static final Map<ClassLoader, AccessControlContext>
CHECKED_CLASS_LOADERS = Collections.synchronizedMap(new WeakHashMap<ClassLoader, AccessControlContext>());
static {
// Install our custom security manager.
if (System.getSecurityManager() != null) {
throw new ExceptionInInitializerError("There's already a security manager set");
}
System.setSecurityManager(new SecurityManager() {
@Override public void
checkPermission(@Nullable Permission perm) {
assert perm != null;
for (Class<?> clasS : this.getClassContext()) {
// Check if an ACC was set for the class.
{
AccessControlContext acc = Sandbox.CHECKED_CLASSES.get(clasS);
if (acc != null) acc.checkPermission(perm);
}
// Check if an ACC was set for the class name.
{
AccessControlContext acc = Sandbox.CHECKED_CLASS_NAMES.get(clasS.getName());
if (acc != null) acc.checkPermission(perm);
}
// Check if an ACC was set for the class loader.
{
AccessControlContext acc = Sandbox.CHECKED_CLASS_LOADERS.get(clasS.getClassLoader());
if (acc != null) acc.checkPermission(perm);
}
}
}
});
}
// --------------------------
/**
* All future actions that are executed through the given {@code clasS} will be checked against the given {@code
* accessControlContext}.
*
* @throws SecurityException Permissions are already confined for the {@code clasS}
*/
public static void
confine(Class<?> clasS, AccessControlContext accessControlContext) {
if (Sandbox.CHECKED_CLASSES.containsKey(clasS)) {
throw new SecurityException("Attempt to change the access control context for '" + clasS + "'");
}
Sandbox.CHECKED_CLASSES.put(clasS, accessControlContext);
}
/**
* All future actions that are executed through the given {@code clasS} will be checked against the given {@code
* protectionDomain}.
*
* @throws SecurityException Permissions are already confined for the {@code clasS}
*/
public static void
confine(Class<?> clasS, ProtectionDomain protectionDomain) {
Sandbox.confine(
clasS,
new AccessControlContext(new ProtectionDomain[] { protectionDomain })
);
}
/**
* All future actions that are executed through the given {@code clasS} will be checked against the given {@code
* permissions}.
*
* @throws SecurityException Permissions are already confined for the {@code clasS}
*/
public static void
confine(Class<?> clasS, Permissions permissions) {
Sandbox.confine(clasS, new ProtectionDomain(null, permissions));
}
// Code for 'CHECKED_CLASS_NAMES' and 'CHECKED_CLASS_LOADERS' omitted here.
}
回答by Black Mantha
The discussion on this question inspired me to start up my own sandbox project.
关于这个问题的讨论激发了我开始我自己的沙箱项目。
https://github.com/Black-Mantha/sandbox
https://github.com/Black-Mantha/sandbox
In it I've come across an important security question: "How do you allow the code outside the sandbox to bypass the SecurityManager
?"
在其中我遇到了一个重要的安全问题:“您如何允许沙箱外的代码绕过SecurityManager
?”
I put the sandbox code in its own ThreadGroup, and always grant permission when outside that group. If you need to run privileged code in that group anyway (in a callback, for example), you can use a ThreadLocal to set a flag for that Thread only. The classloader will prevent the sandbox from accessing the ThreadLocal. Also, if you do this you need to forbid the use of finalizers, since they run in a dedicated thread outside the ThreadGroup.
我将沙箱代码放在它自己的 ThreadGroup 中,并且在该组之外时总是授予权限。如果您无论如何都需要在该组中运行特权代码(例如在回调中),您可以使用 ThreadLocal 仅为该线程设置一个标志。类加载器将阻止沙箱访问 ThreadLocal。此外,如果您这样做,您需要禁止使用终结器,因为它们运行在 ThreadGroup 之外的专用线程中。
回答by Arno Unkrig
After spending a day in the depths of the Java security APIs, I found an amazingly simple solution for executing untrusted code within a sandbox confined by Permissions:
在深入研究 Java 安全 API 一天后,我发现了一个非常简单的解决方案,可以在受权限限制的沙箱中执行不受信任的代码:
Here's the (simplified) source code:
这是(简化的)源代码:
package org.codehaus.commons.compiler;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Policy;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.ProtectionDomain;
public final
class Sandbox {
static {
if (System.getSecurityManager() == null) {
// Before installing the security manager, configure a decent ("positive") policy.
Policy.setPolicy(new Policy() {
@Override public boolean
implies(ProtectionDomain domain, Permission permission) { return true; }
});
System.setSecurityManager(new SecurityManager());
}
}
private final AccessControlContext accessControlContext;
/**
* @param permissions Will be applied on later calls to {@link #confine(PrivilegedAction)} and {@link
* #confine(PrivilegedExceptionAction)}
*/
public
Sandbox(PermissionCollection permissions) {
this.accessControlContext = new AccessControlContext(new ProtectionDomain[] {
new ProtectionDomain(null, permissions)
});
}
/**
* Runs the given <var>action</var>, confined by the permissions configured through the {@link
* #Sandbox(PermissionCollection) constructor}.
*
* @return The value returned by the <var>action</var>
*/
public <R> R
confine(PrivilegedAction<R> action) {
return AccessController.doPrivileged(action, this.accessControlContext);
}
public <R> R
confine(PrivilegedExceptionAction<R> action) throws Exception {
try {
return AccessController.doPrivileged(action, this.accessControlContext);
} catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
}