我计划将 Groovy 脚本引擎集成到我的游戏中,这样它将为游戏提供良好的可修改性,但是如何防止玩家编写邪恶的脚本,例如删除 C: 驱动器上的所有文件?
Groovy 默认包含像
java.io.File
这样的库,因此一旦他们决定编写此类脚本,这将非常容易做到。
我想我无法阻止用户编写类似
while(1==1){}
之类的内容,但至少有没有办法让他们允许删除/修改文件或对电脑造成危险的东西?
Cedric Champeau 有一篇关于自定义 Groovy 编译过程的 博客文章,它的第二部分展示了如何使用
SecureASTCustomizer
和 CompilerConfiguration
来限制脚本可以执行的操作(然后有定义您自己的 AST 检查的示例)对于System.exit
等...
我知道这是一个老问题。我发布此内容是因为它可能会帮助一些人。
我们需要允许最终用户上传 Groovy 脚本并将其作为 Web 应用程序的一部分执行(该应用程序可以执行许多其他操作)。我们担心的是,在这些 Groovy 脚本中,某些用户可能会尝试从文件系统读取文件、读取系统属性、调用 System.exit() 等。
我查看了 http://mrhaki.blogspot.com/2014/04/groovy-goodness-restricting-script.html 但这并不能阻止专家 Groovy 开发人员绕过其他帖子中其他人指出的检查.
然后我尝试让 http://www.sdidit.nl/2012/12/groovy-dsl-executing-scripts-in-sandbox.html 工作,但在运行时设置安全管理器和策略实现不起作用我。我在应用程序服务器启动和网页访问期间不断遇到问题。似乎当策略实现生效时,为时已晚,“CodeSources”(Java 安全术语)已经从默认的 Java 策略文件中获取了访问设置。
然后,我偶然发现了 Ted Neward 撰写的出色白皮书(http://www.tedneward.com/files/Papers/JavaPolicy/JavaPolicy.pdf),该白皮书非常令人信服地解释了(对于我的用例而言)最好的方法是在 JVM 启动时设置策略实现(而不是稍后动态设置)。
下面是对我有用的方法(结合了 Rene 和 Ted 的方法)。顺便说一句:我们正在使用 Groovy 2.3.10。
在 [JDK_HOME]/jre/lib/security/java.security 文件中,将“policy.provider”值设置为“com.yourcompany.security.MySecurityPolicy”。
创建 MySecurityPolicy 类:
import java.net.MalformedURLException;
import java.net.URL;
import java.security.AllPermission;
import java.security.CodeSource;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.Policy;
import java.util.HashSet;
import java.util.Set;
public class MySecurityPolicy extends Policy {
private final Set<URL> locations;
public MySecurityPolicy() {
try {
locations = new HashSet<URL>();
locations.add(new URL("file", "", "/groovy/shell"));
locations.add(new URL("file", "", "/groovy/script"));
} catch (MalformedURLException e) {
throw new IllegalStateException(e);
}
}
@Override
public PermissionCollection getPermissions(CodeSource codeSource) {
// Do not store these in static or instance variables. It won't work. Also... they're cached by security infrastructure ... so this is okay.
PermissionCollection perms = new Permissions();
if (!locations.contains(codeSource.getLocation())) {
perms.add(new AllPermission());
}
return perms;
}
}
将 MySecurityPolicy 打包并放入 [JDK_HOME]/jre/lib/ext 目录中。
将“-Djava.security.manager”添加到JVM启动选项中。您不需要提供自定义安全管理器。默认的就可以了。
“-Djava.security.manager”选项为整个应用程序启用 Java 安全管理器。应用程序及其所有依赖项将具有“AllPermission”,因此将被允许执行任何操作。
Groovy 脚本在“/groovy/shell”和“/groovy/script”“CodeSources”下运行。它们不一定是文件系统上的物理目录。上面的代码没有给 Groovy 脚本任何权限。
用户仍然可以执行以下操作:
Thread.currentThread().interrupt();
while (true) {}(无限循环)
您可以将以下内容(在运行时动态地)添加到每个脚本的开头,然后将其传递到 Groovy shell 执行:
@ThreadInterrupt
import groovy.transform.ThreadInterrupt
@TimedInterrupt(5)
import groovy.transform.TimedInterrupt
这些内容在 http://www.jroller.com/melix/entry/upcoming_groovy_goodness_automatic_thread
进行了解释第一个处理“Thread.currentThread().interrupt()”更优雅一些(但它不会阻止用户中断线程)。也许,你可以使用 AST 来在某种程度上防止中断。在我们的例子中,这不是一个大问题,因为每个 Groovy 脚本执行都在自己的线程中运行,如果坏人希望杀死自己的线程,他们可能会把自己击倒。
第二个可以防止无限循环,因为所有脚本都会在 5 秒后超时。您可以调整时间。
请注意,我注意到 Groovy 脚本执行时间的性能下降,但没有注意到 Web 应用程序其余部分的性能显着下降。
希望有帮助。
查看 SecurityContext 类。
Groovy Web Console 似乎已经解决了这个问题,因为它不会执行类似
System.exit(1)
的操作。 源代码可在 GitHub 上找到,因此您可以了解他们是如何做到的。
如果您不确定从哪里开始,我建议与作者联系,他应该能够为您指明正确的方向。