为什么Java安全管理器不禁止创建新的Thread()并启动它?

13

您知道为什么Java安全管理器不禁止创建新线程或启动它们吗?new FileWriter在安全管理器下,但new Thread()和threadInstance.start()都不在安全管理器下,并且可以被调用。

  1. 禁止创建和启动线程会有用吗?
  2. 实现起来难吗?
  3. 创建和启动新线程并不是禁止的关键吗?

哪种编程语言? - user330315
Java,我已经添加到主题和描述中。 - bastiat
2个回答

20

无法定义一项安全策略来防止代码使用标准的Java SecurityManager创建和启动新线程。

假设你有以下代码:

public class Test {
  public static void main(String [] args) {
    System.out.println(System.getSecurityManager() != null ? "Secure" : "");
    Thread thread = new Thread(
      new Runnable() { 
        public void run() {
          System.out.println("Ran");
        }
    });
    thread.start();
  }
}

你可以使用以下命令来运行它:

java -Djava.security.manager -Djava.security.policy==/dev/null Test

它将正常运行并输出:

Secure
Ran

即使我们将安全策略设置为/dev/null,这会向任何代码授予零权限。因此,不可能授予更少的权限来防止该代码创建线程。

这是因为标准的java.lang.SecurityManager只有在代码尝试在根ThreadGroup中创建线程时才执行权限检查。同时,SecurityManager的getThreadGroup方法总是返回当前线程的线程组,这永远不会是根线程组,因此总是会被授予创建新线程的权限。

解决方法之一是子类化java.lang.SecurityManager并覆盖getThreadGroup方法以返回根ThreadGroup。这将允许您根据是否具有java.lang.RuntimePermission“modifyThreadGroup”来控制代码是否可以创建线程。

因此,现在如果我们定义一个SecurityManager的子类如下:

public class ThreadSecurityManager extends SecurityManager { 
  
  private static ThreadGroup rootGroup;
  
  @Override
  public ThreadGroup getThreadGroup() {
    if (rootGroup == null) {
      rootGroup = getRootGroup();
    }
    return rootGroup;
  }

  private static ThreadGroup getRootGroup() {
    ThreadGroup root =  Thread.currentThread().getThreadGroup();
    while (root.getParent() != null) {
     root = root.getParent();
    }
    return root;
  }
}

接着再次运行我们的命令,但这次需要指定我们的子类化ThreadSecurityManager:

java -Djava.security.manager=ThreadSecurityManager -Djava.security.policy==/dev/null Test

当我们尝试创建新线程时,我们的测试类会抛出异常:

Exception in thread "main" java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "modifyThreadGroup")

3
在线程构造函数中执行了访问检查,以查看调用者是否有更改新线程将添加到的线程组的权限。这就是您实施安全策略禁止创建新线程的方式。
(还有另一个检查线程组创建...检查您是否有权限将新组添加到其父对象。)
所以回答您的问题:
为什么Java安全管理器既不禁止创建新的Thread()也不禁止启动它?
原因是您JVM当前的安全策略允许父线程修改其线程组。您应该能够修改该策略设置以防止这种情况,并因此防止创建子线程。
禁止可能很有用吗?
是的。允许不受信任的代码创建/启动线程是不明智的,因为:1)一旦启动线程就无法安全地终止,2)创建/启动大量线程可能使JVM (和可能是操作系统)崩溃。
这很难实现吗?
从您的角度来看,只需更改策略即可。

有很多方法可以让Java虚拟机崩溃。您将需要为不受信任的代码提供对至少一些线程的访问权限。能够创建线程非常有用。 - Tom Hawtin - tackline
1
@Tom 通常情况下,您不需要授予线程访问权限,但在这种情况下,您经常需要授予替换设施的访问权限。这并不是太糟糕,只是因为Java的异步IO处理仍然不是很强大。 - Donal Fellows
1
在安全管理器下创建新线程时,我可能会遇到什么情况导致异常?我已经尝试过在我的主Java中使用空的app.policy(-Djava.security.manager -Djava.security.policy = app.policy -cp bin pl.com.App),以及在启用了安全性的Tomcat中的servlet中(catalina.policy中没有Thread权限),但我总是可以创建和启动新线程而不会出现异常。 - bastiat
原因是您的JVM当前的安全策略允许父线程修改其ThreadGroup。这在哪里有记录?默认的策略管理器在大多数情况下的态度是禁止未明确允许的所有操作。 - bastiat
1
安全检查的文档在此处记录:http://docs.oracle.com/javase/6/docs/api/java/lang/SecurityManager.html#checkAccess(java.lang.ThreadGroup) - Stephen C

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接