在允许用户提交他们自己的代码以由服务器运行的模拟服务器环境中,任何用户提交的代码在沙箱旁边运行显然是有利的,这与浏览器内的小应用程序没有什么不同。我希望能够利用JVM本身,而不是添加另一个VM层来隔离这些提交的组件。
使用现有的Java沙箱模型,这种限制似乎是可能的,但是有没有一种动态的方法来支持运行中的应用程序中用户提交的部分呢?
发布于 2014-08-26 10:32:09
要解决接受答案中的问题,即自定义SecurityManager
将应用于JVM中的所有线程,而不是基于每个线程,您可以创建一个自定义SecurityManager
,它可以为特定线程启用/禁用,如下所示:
import java.security.Permission;
public class SelectiveSecurityManager extends SecurityManager {
private static final ToggleSecurityManagerPermission TOGGLE_PERMISSION = new ToggleSecurityManagerPermission();
ThreadLocal<Boolean> enabledFlag = null;
public SelectiveSecurityManager(final boolean enabledByDefault) {
enabledFlag = new ThreadLocal<Boolean>() {
@Override
protected Boolean initialValue() {
return enabledByDefault;
}
@Override
public void set(Boolean value) {
SecurityManager securityManager = System.getSecurityManager();
if (securityManager != null) {
securityManager.checkPermission(TOGGLE_PERMISSION);
}
super.set(value);
}
};
}
@Override
public void checkPermission(Permission permission) {
if (shouldCheck(permission)) {
super.checkPermission(permission);
}
}
@Override
public void checkPermission(Permission permission, Object context) {
if (shouldCheck(permission)) {
super.checkPermission(permission, context);
}
}
private boolean shouldCheck(Permission permission) {
return isEnabled() || permission instanceof ToggleSecurityManagerPermission;
}
public void enable() {
enabledFlag.set(true);
}
public void disable() {
enabledFlag.set(false);
}
public boolean isEnabled() {
return enabledFlag.get();
}
}
ToggleSecurirtyManagerPermission
只是java.security.Permission
的一个简单实现,以确保只有授权的代码才能启用/禁用安全管理器。它看起来是这样的:
import java.security.Permission;
public class ToggleSecurityManagerPermission extends Permission {
private static final long serialVersionUID = 4812713037565136922L;
private static final String NAME = "ToggleSecurityManagerPermission";
public ToggleSecurityManagerPermission() {
super(NAME);
}
@Override
public boolean implies(Permission permission) {
return this.equals(permission);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ToggleSecurityManagerPermission) {
return true;
}
return false;
}
@Override
public int hashCode() {
return NAME.hashCode();
}
@Override
public String getActions() {
return "";
}
}
发布于 2014-06-15 07:07:20
以下是针对该问题的线程安全解决方案:
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.
}
请评论!
CU
阿诺
发布于 2009-02-02 05:44:17
您可能需要使用自定义的SecurityManger和/或AccessController。有关更多详细信息,请参阅来自Sun的Java Security Architecture和other security documentation。
https://stackoverflow.com/questions/502218
复制相似问题