gpt4 book ai didi

java - 如何创建安全的 JEXL(脚本)沙箱?

转载 作者:塔克拉玛干 更新时间:2023-11-03 05:03:49 28 4
gpt4 key购买 nike

我正在为 JEXL 脚本创建一个沙箱,以便恶意用户无法访问我们授予他们访问权限的变量之外的数据,也无法对服务器执行 DOS 攻击。我想为同样这样做的其他人记录下来,并征求其他人对该方法的意见。

以下是我所知道的需要解决的问题的列表:

  1. 只允许使用白名单上的“new”实例化类。
  2. 不允许访问任何类的 getClass 方法,因为这样可以调用 forName 并且可以访问任何类。
  3. 限制对文件等资源的访问。
  4. 只允许表达式执行一定的时间,这样我们就可以限制它消耗的资源量。

这不适用于 JEXL,但可能适用于您正在使用的脚本语言:

  1. 不允许对象具有自定义的 finalize 方法,因为 finalize 方法是从终结器线程调用的,并且将使用原始 AccessControlContext 而不是用于创建对象并执行其中代码的方法执行。<

最佳答案

更新:这一切都是使用 JEXL 2.0.1 完成的。您可能需要对此进行调整以使其适用于较新的版本。

以下是我处理每种情况的方法。我已经创建了单元测试来测试这些案例中的每一个,并且我已经验证了它们是否有效。

  1. JEXL 使这变得非常简单。只需创建一个自定义的 ClassLoader。覆盖两个 loadClass() 方法。在 JexlEngine 上调用 setClassLoader()。

  2. 同样,JEXL 使这变得非常简单。您必须同时阻止“.class”和“.getClass()”。创建您自己的扩展 UberspectImpl 的 Uberspect 类。覆盖 getPropertyGet,如果标识符等于“类”,则返回 null。覆盖 getMethod,如果方法等于“getClass”,则返回 null。在构造 JexlEngine 时传递对 Uberspect 实现的引用。

    class MyUberspectImpl extends UberspectImpl {

    public MyUberspectImpl(Log jexlLog) {
    super(jexlLog);
    }

    @Override
    public JexlPropertyGet getPropertyGet(Object obj, Object identifier, JexlInfo info) {
    // for security we do not allow access to .class property
    if ("class".equals(identifier)) throw new RuntimeException("Access to getClass() method is not allowed");
    JexlPropertyGet propertyGet = super.getPropertyGet(obj, identifier, info);
    return propertyGet;
    }

    @Override
    public JexlMethod getMethod(Object obj, String method, Object[] args, JexlInfo info) {
    // for security we do not allow access to .getClass() method
    if ("getClass".equals(method)) throw new RuntimeException("Access to getClass() method is not allowed");
    return super.getMethod(obj, method, args, info);
    }

    }
  3. 您可以使用 Java 的 AccessController 机制来执行此操作。我将简要介绍一下这样做的过程。使用 -Djava.security.policy=policyfile 启动 java。创建一个名为 policyfile 的文件,其中包含以下行:授予 { 权限 java.security.AllPermission; };通过以下调用设置默认的 SecurityManager:System.setSecurityManager(new SecurityManager());现在您可以控制权限,默认情况下您的应用程序拥有所有权限。当然,如果您将应用程序的权限限制在它需要的范围内,那会更好。接下来,创建一个将权限限制在最低限度的 AccessControlContext 并调用 AccessController.doPrivileged() 并传递 AccessControlContext,然后在 doPrivileged() 中执行 JEXL 脚本。这是一个演示这一点的小程序。 JEXL 脚本调用 System.exit(1),如果它没有包含在 doPrivileged() 中,它将成功终止 JVM。

    System.out.println("java.security.policy=" + System.getProperty("java.security.policy"));
    System.setSecurityManager(new SecurityManager());
    try {
    Permissions perms = new Permissions();
    perms.add(new RuntimePermission("accessDeclaredMembers"));
    ProtectionDomain domain = new ProtectionDomain(new CodeSource( null, (Certificate[]) null ), perms );
    AccessControlContext restrictedAccessControlContext = new AccessControlContext(new ProtectionDomain[] { domain } );

    JexlEngine jexlEngine = new JexlEngine();
    final Script finalExpression = jexlEngine.createScript(
    "i = 0; intClazz = i.class; "
    + "clazz = intClazz.forName(\"java.lang.System\"); "
    + "m = clazz.methods; m[0].invoke(null, 1); c");

    AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
    @Override
    public Object run() throws Exception {
    return finalExpression.execute(new MapContext());
    }
    }, restrictedAccessControlContext);
    }
    catch (Throwable ex) {
    ex.printStackTrace();
    }
  4. 这样做的诀窍是在脚本完成之前中断它。我发现这样做的一种方法是创建自定义 JexlArithmetic 类。然后覆盖该类中的每个方法,并在调用父类(super class)中的实际方法之前检查脚本是否应停止执行。我正在使用 ExecutorService 创建线程。当调用 Future.get() 时传递等待的时间量。如果抛出 TimeoutException,调用 Future.cancel() 会中断运行脚本的线程。在新 JexlArithmetic 类的每个重写方法中检查 Thread.interrupted(),如果为真则抛出 java.util.concurrent.CancellationException。是否有更好的位置来放置将在脚本执行时定期执行的代码,以便它可以被中断?

这是 MyJexlArithmetic 类的摘录。您必须添加所有其他方法:

    public class MyJexlArithmetic extends JexlArithmetic {

public MyJexlArithmetic(boolean lenient) {
super(lenient);
}

private void checkInterrupted() {
if (Thread.interrupted()) throw new CancellationException();
}

@Override
public boolean equals(Object left, Object right) {
checkInterrupted();
return super.equals(left, right); //To change body of generated methods, choose Tools | Templates.
}

@Override
public Object add(Object left, Object right) {
checkInterrupted();
return super.add(left, right);
}
}

下面是我如何实例化 JexlEngine:

        Log jexlLog = LogFactory.getLog("JEXL");
Map <String, Object> functions = new HashMap();
jexlEngine = new JexlEngine(new MyUberspectImpl(jexlLog), new MyJexlArithmetic(false), functions, jexlLog);

关于java - 如何创建安全的 JEXL(脚本)沙箱?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6551073/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com