蹲厕所的熊

benjaminwhx

Java安全管理器-SecurityManager

2018-06-17 作者: 吴海旭


  1. 前言
  2. 开启安全管理
  3. policy file

前言

相信很多人在看JDK、Spring、Tomcat源码的时候会发现一个类的踪影,它就是SecurityManager。它经常在代码中以这样的方式出现:

SecurityManager sm = getSecurityManager();
if (sm != null) {
    sm.checkPropertyAccess(key);
}

这个功能在我们写代码的时候可以说几乎不会用上,但是为了知识体系的完整,我们还是需要了解什么是SecurityManager。

SecurityManager:安全管理器,它是防范恶意攻击的主要安全卫士。它通过执行运行阶段检查和访问授权,以实施应用所需的安全策略,从而保护资源免受恶意操作的攻击。而它需要根据提供的Java安全策略文件(java policy file)来进行防护。

开启安全管理

默认我们系统中直接运行的Java程序都没有开启SecurityManager,开启它只需要在JVM启动参数中加上 -Djava.security.manager 即可,JVM会在程序启动的时候在 sun.misc.Launcher 类中进行初始化。

public Launcher() {
    // ...ignore

    // 获取配置的JVM参数 -Djava.security.manager
    String var2 = System.getProperty("java.security.manager");
    if (var2 != null) {
        SecurityManager var3 = null;
        if (!"".equals(var2) && !"default".equals(var2)) {
            try {
                var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
            } catch (Exception var5) {}
        } else {
            // 使用默认的SecurityManager
            var3 = new SecurityManager();
        }
        // 设置安全管理器
        System.setSecurityManager(var3);
    }
}

既然系统也是通过System.setSecurityManager的方式设置安全管理器的,我们在测试的时候也可以手动设置,但是还是推荐使用启动参数的方式来进行设置。

policy file

默认开启安全管理类后,JVM会先去 ${java.home}/jre/lib/security 下找到 java.security 获取到其中的一段配置:

# The default is to have a single system-wide policy file,
# and a policy file in the user's home directory.
policy.url.1=file:${java.home}/lib/security/java.policy
policy.url.2=file:${user.home}/.java.policy

这段配置就是policy文件的目标位置,默认位置为 ${java.home}/lib/security/java.policy

打开此文件:

// Standard extensions get all permissions by default

grant codeBase "file:${{java.ext.dirs}}/*" {
        permission java.security.AllPermission;
};

// default permissions granted to all domains

grant {
        // Allows any thread to stop itself using the java.lang.Thread.stop()
        // method that takes no argument.
        // Note that this permission is granted by default only to remain
        // backwards compatible.
        // It is strongly recommended that you either remove this permission
        // from this policy file or further restrict it to code sources
        // that you specify, because Thread.stop() is potentially unsafe.
        // See the API specification of java.lang.Thread.stop() for more
        // information.
        permission java.lang.RuntimePermission "stopThread";

        // allows anyone to listen on dynamic ports
        permission java.net.SocketPermission "localhost:0", "listen";

        // "standard" properies that can be read by anyone

        permission java.util.PropertyPermission "java.version", "read";
        permission java.util.PropertyPermission "java.vendor", "read";
        permission java.util.PropertyPermission "java.vendor.url", "read";
        permission java.util.PropertyPermission "java.class.version", "read";
        permission java.util.PropertyPermission "os.name", "read";
        permission java.util.PropertyPermission "os.version", "read";
        permission java.util.PropertyPermission "os.arch", "read";
        permission java.util.PropertyPermission "file.separator", "read";
        permission java.util.PropertyPermission "path.separator", "read";
        permission java.util.PropertyPermission "line.separator", "read";

        permission java.util.PropertyPermission "java.specification.version", "read";
        permission java.util.PropertyPermission "java.specification.vendor", "read";
        permission java.util.PropertyPermission "java.specification.name", "read";

        permission java.util.PropertyPermission "java.vm.specification.version", "read";
        permission java.util.PropertyPermission "java.vm.specification.vendor", "read";
        permission java.util.PropertyPermission "java.vm.specification.name", "read";
        permission java.util.PropertyPermission "java.vm.version", "read";
        permission java.util.PropertyPermission "java.vm.vendor", "read";
        permission java.util.PropertyPermission "java.vm.name", "read";
};

文件中分两部分授权:

  1. ext下的文件全部授权。
  2. 其他文件授予特定的权限,比如线程可以调用 stop 方法(permission java.lang.RuntimePermission “stopThread”),可以读取系统的 java.version 属性(permission java.util.PropertyPermission “java.version”, “read”)等等。

我们写一个例子尝试授权是否能生效:

public static void main(String[] args) throws Exception {
    Thread t = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    t.start();

    t.stop();
    System.out.println("stop finish");
}

注释掉java.policy里stopThread的那行,执行main方法,会出现拒绝访问的exception:

Exception in thread "main" java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "stopThread")
    at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
    at java.security.AccessController.checkPermission(AccessController.java:884)
    at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
    at java.lang.Thread.stop(Thread.java:843)
    at com.github.SecurityManagerTest.main(SecurityManagerTest.java:38)

当然,我们也可以使用自己定义的policy文件,只需要在JVM的启动参数中加上 -Djava.security.policy=/Users/Benjamin/Desktop/my.policy 即可。等号后面是自定义的policy文件地址。



坚持原创技术分享,您的支持将鼓励我继续创作!



分享

评论