Java:阻止System.exit、文件访问等。。。对于用户定义的表达式

Java:阻止System.exit、文件访问等。。。对于用户定义的表达式,java,Java,在我们的应用程序中,用户能够定义由我们的主引擎执行的Java表达式(这些表达式只是方法调用:例如,Math.abs(42))。它们是通过反射来执行的 有哪些不同的解决方案可以防止这些表达式直接调用System.exit(以及文件访问和其他…),或者通过内部调用System.exit的方法调用 注意,几个不同的表达式可以在不同的线程中执行。例如,使用SecurityManager阻止文件访问不起作用,因为在执行表达式时,主机必须仍然能够访问文件系统。您所做的事情太疯狂了。允许用户在您的机器上执行任

在我们的应用程序中,用户能够定义由我们的主引擎执行的Java表达式(这些表达式只是方法调用:例如,Math.abs(42))。它们是通过反射来执行的

有哪些不同的解决方案可以防止这些表达式直接调用System.exit(以及文件访问和其他…),或者通过内部调用System.exit的方法调用


注意,几个不同的表达式可以在不同的线程中执行。例如,使用SecurityManager阻止文件访问不起作用,因为在执行表达式时,主机必须仍然能够访问文件系统。

您所做的事情太疯狂了。允许用户在您的机器上执行任意Java代码是一个糟糕的想法,您无法保证这一点,以防黑客知道他们的东西

我能想到的唯一解决方案是创建可能使用的包和方法的白名单。但试图将特定行为列入黑名单永远不会让你达到目的。总有比你更聪明的人来打破东西


更新:我现在已经阅读了更多关于
SecurityManager
的内容,似乎你可以很好地控制包访问,所以我建议你接受欧内斯特的回答。

你说你不能使用
SecurityManager
——这正是它的用途:托管不可信的代码,在小程序容器或RMI服务器中。现代SecurityManager配置了策略文件,这些文件授予特定的细粒度权限,包括对文件系统的有限访问。您需要使用SecurityManager,但需要成为这方面的专家


这是一个巨大的话题;最好的办法是在谷歌上搜索“Java安全策略文件”,然后尽可能地阅读所有内容。

Java安全系统是实现这一点的合适工具
SecurityManager
只是您必须使用的系统的一部分

基本上,您自己不会调用不受信任的代码。相反,您将把该调用包装成一个
PrivilegedAction
,并将其交给一个
AccessController.doPrivileged
方法。然后,不受信任的代码将在另一个
ProtectionDomain
中执行

因此,您必须配置两个保护域:一个用于具有完全权限的引擎,另一个用于具有降低权限的不受信任代码


但正如欧内斯特已经提到的:这是相当复杂的东西,不适合这样的问答网站。阅读Oracle和Co.提供的相应教程。使用上述关键字进行搜索。

例如,Apache Velocity也有类似的问题需要解决。它们使用velocity.properties中定义的黑名单类和包:

# ----------------------------------------------------------------------------
# SECURE INTROSPECTOR
# ----------------------------------------------------------------------------
# If selected, prohibits methods in certain classes and packages from being 
# accessed.
# ----------------------------------------------------------------------------

introspector.restrict.packages = java.lang.reflect

# The two most dangerous classes

introspector.restrict.classes = java.lang.Class
introspector.restrict.classes = java.lang.ClassLoader

# Restrict these for extra safety

introspector.restrict.classes = java.lang.Compiler
introspector.restrict.classes = java.lang.InheritableThreadLocal
introspector.restrict.classes = java.lang.Package
introspector.restrict.classes = java.lang.Process
introspector.restrict.classes = java.lang.Runtime
introspector.restrict.classes = java.lang.RuntimePermission
introspector.restrict.classes = java.lang.SecurityManager
introspector.restrict.classes = java.lang.System
introspector.restrict.classes = java.lang.Thread
introspector.restrict.classes = java.lang.ThreadGroup
introspector.restrict.classes = java.lang.ThreadLocal

Java中有太多东西可能会导致您可能没有意识到的问题。最安全的做法是创建一个允许的类列表,并在单独的进程中通过自定义类装入器运行这些类(这样您就可以安全地终止它)

对于大多数人都不知道的严重危险代码,有一个可以让你

  • 访问内存的随机区域
  • 在不调用构造函数的情况下创建类的新实例,例如枚举的新实例
  • 离散地锁定和解锁对象监视器。(无同步块)

我同意这种观点——这是一件可怕的事情——但在我看来,自己滚动比使用平台更不安全。@ErnestFriedman Hill我之所以这么说,是因为我不知道SecurityManager的细粒度访问控制有多好。