使用SecurityManager会有什么性能惩罚吗?

9
使用SecurityManager会有性能损失吗?
我需要以下内容:
public class ExitHelper {

    public ExitHelper() {

        System.setSecurityManager(new ExitMonitorSecurityManager());

    }

    private static class ExitMonitorSecurityManager extends SecurityManager {

        @Override
        public void checkPermission(Permission perm) {}

        @Override
        public void checkPermission(Permission perm, Object context) {}

        @Override
        public void checkExit( final int status ) {
            // this is the part I need and I don't care much about the performance issue of this method
        }
}

这会对我的程序产生很大的影响吗?

该程序会打开许多文件,例如。如果我启用SecurityManager并在其中添加一些日志记录,可以看到这些方法被频繁调用。真的很多。因此,看起来放置SecurityManager意味着会进行大量的调用。它比默认的SecurityManager慢吗?(默认情况下是否有SecurityManager?)

这是如何工作的?哪个部分的程序将被检查权限以及检查频率?我对两个checkPermission(...)方法感到担忧。


这里有同样的问题:http://stackoverflow.com/questions/3656328/does-java-security-manager-decrease-performance - A.H.
3个回答

7

有性能惩罚,但可能很小,因为:

  • 它仅适用于您尝试需要权限检查的某种活动时。
  • 大多数需要权限检查的操作是昂贵的操作(IO、网络访问等),因此安全检查的开销可能只占总运行时间的一小部分。
  • 检查本身可以非常便宜。

特别要注意的是,Java库代码中安全检查的调用代码通常是非常轻量级的,例如:

 SecurityManager security = System.getSecurityManager();
 if (security != null) {
     security.checkXXX(argument,  . . . );
 }

如果您的安全管理器代码本身同样轻量级,则安全检查的运行时成本应该是可忽略的。然而,我建议不要在SecurityManager本身中加入任何日志记录代码 - 这将是昂贵的,并且可能属于应用程序代码的更高级别。
如果您希望绝对最小化您不关心的权限的安全管理器开销,那么您应该覆盖您不需要的特定checkXXX方法,例如:
@Override 
public void checkRead(String file) {
  // empty method as we are happy to allow all file reads
}

最终,您将需要为自己的特定情况进行基准测试,但“直觉”回答是您不应该真正担心它。


2

是的,会有性能损失。如果你关心这个问题,唯一的解决方法就是进行测量,看看损失是否过高。

对于你特定的使用情况,一个潜在的解决方案是如果你可以缩小需要的范围。显然,你想要阻止一些代码退出应用程序,但无法控制该代码。如果你知道何时可能调用该代码,那么就可以在该调用期间设置安全管理器(请注意,由于安全管理器设置是全局的,因此你需要注意线程影响),例如:

System.setSecurityManager(new ExitMonitorSecurityManager());
try {
  // ... do protected op here ...
} finally {
  System.setSecurityManager(null);
}

更新:

为了向后来查看此答案的人澄清,本答案不适用于处理可能存在恶意代码的情况。在这种情况下,应始终使用适当配置的SecurityManager。本答案假定OP试图处理一个编写不佳的第三方库,在某个明确定义的时间点调用System.exit()。


1
不要啊!!!!!你不知道代码何时会被调用。除了可能有多个线程需要同时运行未受信任的代码外,恶意代码还可以运行终结器。 - Tom Hawtin - tackline
@TomHawtin-tackline - 嗯?如果你知道即将调用的特定库调用会执行你不想要的操作,那么你可以使用类似这样的东西(同意由于各种原因它并不理想)。这是假设你没有运行恶意代码,只是行为不良的代码。如果你正在运行潜在的恶意代码,那么你应该有一个全职安全管理员,结束故事。我很确定OP指的是前一种情况,而不是后一种情况。 - jtahlborn
@TomHawtin-tackline - 我已经更新了答案以澄清上下文。 - jtahlborn

2
我有一些经验证据可以在实施安全管理器问题后做出贡献:一个与没有安全管理器完全相同的Java SecurityManager,除了对System.exit进行单个检查调整。使用这个匿名内部类的性能影响非常大:
        System.setSecurityManager(new SecurityManager() {
            @Override
            public void checkPermission(Permission perm) {
                return; // no security manager behaviour
            }

            @Override
            public void checkPermission(Permission perm, Object context) {
                return; // no security manager behaviour
            }

            @Override
            public void checkExit(int status) {
                Thread.dumpStack();
                super.checkExit(status);
            }
        });

在Eclipse中启动我的应用程序后,我的经验是它明显变慢了,我在同事的电脑上确认了这一点。因此,我认为“微不足道”可能是一种低估(而且我的使用情况实际上并没有执行任何检查!)。将其视为一个轶事,说明这不是事实。另外注意一点:我创建了一个最终类,其中包含所有方法的无操作检查,以避免实例化权限对象等(并鼓励jit编译器进行热线连接)。使用这种方法,性能影响确实很小。因此,对于想要添加几个特定检查(而不依赖于Java策略)的人来说,这实际上的确具有微不足道的影响:
public final class SystemExitTraceSecurityManager extends SecurityManager {

    @Override
    public final void checkAccept(String host, int port) {
    }

    @Override
    public final void checkAccess(Thread t) {
    }

    @Override
    public final void checkAccess(ThreadGroup g) {
    }

    @Override
    public final void checkAwtEventQueueAccess() {
    }

    @Override
    public final void checkConnect(String host, int port) {
    }

    @Override
    public final void checkConnect(String host, int port, Object context) {
    }

    @Override
    public final void checkCreateClassLoader() {
    }

    public final void checkDelete(String file) {
    };

    @Override
    public final void checkExec(String cmd) {
    }

    public final void checkExit(int status) {
        Thread.dumpStack();
    };

    @Override
    public final void checkLink(String lib) {
    }

    @Override
    public final void checkListen(int port) {
    }

    @Override
    public final void checkMemberAccess(Class<?> clazz, int which) {
    }

    @Override
    public final void checkMulticast(InetAddress maddr) {
    }

    @Override
    public final void checkMulticast(InetAddress maddr, byte ttl) {
    }

    @Override
    public final void checkPackageAccess(String pkg) {
    }

    @Override
    public final void checkPackageDefinition(String pkg) {
    }

    @Override
    public final void checkPermission(Permission perm) {
    }

    @Override
    public final void checkPermission(Permission perm, Object context) {
    }

    @Override
    public final void checkPrintJobAccess() {
    }

    @Override
    public final void checkPropertiesAccess() {
    }

    public final void checkPropertyAccess(String key) {
    };

    @Override
    public final void checkRead(FileDescriptor fd) {
    }

    @Override
    public final void checkRead(String file) {
    }

    @Override
    public final void checkRead(String file, Object context) {
    }

    @Override
    public final void checkSecurityAccess(String target) {
    }

    @Override
    public final void checkSetFactory() {
    }

    @Override
    public final void checkSystemClipboardAccess() {
    }

    @Override
    public final boolean checkTopLevelWindow(Object window) {
        return true;
    }

    @Override
    public final void checkWrite(FileDescriptor fd) {
    }

    @Override
    public final void checkWrite(String file) {
    }
}

2
注意:由于JEP 232,从JDK 9开始性能影响不应该那么严重。 - Uux

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