JSR-223的javax.script包的一个大问题是缺乏任何明显的方法来沙箱运行的脚本。所以显而易见的问题是:您如何对 JSR-223 脚本进行沙箱处理?有人问过这个问题,甚至有人尝试过回答。
这里有两个关于这个问题的有趣问题,但遗憾的是没有抓住重点:
关键在于,这不仅仅是设置正确的安全策略或使用正确的 ClassLoader 的问题,因为您尝试保护的代码不是 Java 代码,它没有类。您可以尝试通过使用 ClassLoader 为 ScriptEngine 提供特殊的 ProtectionDomain 来保护 ScriptEngine,但这只有在系统 ClassLoader 无法找到 ScriptEngine 通过加载具有错误 ProtectionDomain 的类来挫败您的努力时才有效,这是绑定的任何作为 JRE 的一部分被有用地包含在内的 ScriptEngine 都会发生这种情况。
这是另一个看起来不错但没有抓住重点的资源:Simple JVM sandboxing。它告诉粗心的人,他们可以通过doPrivileged
在自定义 AccessControlContext 中使用包含很少权限的 ProtectionDomain 来沙箱化他们的脚本,但是这样做当然没有意义,因为doPrivileged
仅对获得权限有用,而对拒绝权限没有用。如果不受信任的代码已经在沙箱 ProtectionDomain 中,那么该doPrivileged
技巧将不会做任何事情,如果不受信任的代码在非沙箱 ProtectionDomain 中,那么它可以调用doPrivileged
并完全绕过沙箱尝试。
真正的问题是如何解决这些问题?假设我们打算使用 ProtectionDomains,似乎唯一的选择是给 ScriptEngineManager 一个自定义的 ClassLoader,它故意从系统 ClassLoader 中隐藏一些类。在这种情况下,我们如何决定将哪些类放入沙箱以及从系统 ClassLoader 中获取哪些类?似乎没有任何可靠的方法可以知道哪些类可能负责为脚本提供突破沙箱的方法,尤其是对于尚不存在的 ScriptEngines。
我能想到的唯一选择是我真正想问的。简单地忽略 ProtectionDomains 并实现具有用于评估脚本的沙盒模式的自定义 SecurityManager 会是更好的解决方案吗?例如:
public final class SandboxMan extends SecurityManager {
private int sandboxDepth = 0;
@Override public void checkPermission(Permission permission) {
if(sandboxDepth > 0) throw new SecurityException("Sandboxed: " + permission);
else super.checkPermission(permission);
}
@Override public void checkPermission(Permission permission, Object context) {
if(sandboxDepth > 0) throw new SecurityException("Sandboxed: " + permission);
else super.checkPermission(permission, context);
}
public Object eval(ScriptEngine engine, String script) throws ScriptException {
if(sandboxDepth == Integer.MAX_VALUE) throw new SecurityException("Sandbox depth");
sandboxDepth++;
try {
return engine.eval(script);
} finally { sandboxDepth--; }
}
}
这看起来既棘手又危险。在涉及安全时试图变得棘手是很危险的,但是考虑到这种情况,这真的是最好的解决方案吗?