转载

Java SecurityManager初识

最近整理文档的时候,发现在角落里躺着几篇有关Java SecurityManager的文章,最近也没有精力研究Java安全了,就将文章分享出来。

简介

根据Oracle官方对 The Security Manager 的说明:

A security manager is an object that defines a security policy for an application. This policy specifies actions that are unsafe or sensitive. Any actions not allowed by the security policy cause a SecurityException to be thrown. An application can also query its security manager to discover which actions are allowed. Typically, a web applet runs with a security manager provided by the browser or Java Web Start plugin. Other kinds of applications normally run without a security manager, unless the application itself defines one. If no security manager is present, the application has no security policy and acts without restrictions.

翻译为中文的意思是: Security Manager 是用于对一个应用程序定义一个安全策略的对象。这个策略能够定义一些不安全或者是敏感操作。不被安全策略允许的操作将会抛出 SecurityException 。同样地,利用 Security Manager 还可以定义一些允许执行的操作。默认情况下, oracle 已经对 Security Manager 能够控制的操作进行了说明 Permissions in the Java Development Kit (JDK) ,包括 Socket 、文件、序列化、反射等权限。

Security Manager 主要是在运行时检查。默认情况下,直接运行的Java程序都没有开启SecurityManager。如果程序开启了 SecurityManager ,那么程序所进行的任何操作最终都会进入到 Security Manager 进行检查判断。Java程序有两种方式开启 Security Manager 以及定义操作。这两种方式都会在本文中进行说明。

SecurityManager
SecurityManager

自定义 SecurityManager 策略文件

虽然Java程序默认没有开启 Security Manager ,但是我们可以通过在JVM启动参数中加上 -Djava.security.manager 开启。具体用法是 java -Djava.security.manager classfile 。开启之后,JVM首先会去 ${java.home}/jre/lib/security 寻找 java.security 文件。在 java.security 定义了很多与安全相关的配置。例如

# List of comma-separated packages that start with or equal this string
# will cause a security exception to be thrown when
# passed to checkPackageAccess unless the
# corresponding RuntimePermission ("accessClassInPackage."+package) has
# been granted.
package.access=sun.,/
               com.sun.xml.internal.,/
               com.sun.imageio.,/
               com.sun.istack.internal.,/
               com.sun.jmx.,/
               com.sun.media.sound.,/
               com.sun.naming.internal.,/
               com.sun.proxy.,/
               com.sun.corba.se.,/
               com.sun.org.apache.bcel.internal.,/

....
# List of comma-separated packages that start with or equal this string
# will cause a security exception to be thrown when
# passed to checkPackageDefinition unless the
# corresponding RuntimePermission ("defineClassInPackage."+package) has
# been granted.
#
# by default, none of the class loaders supplied with the JDK call
# checkPackageDefinition.
#
package.definition=sun.,/
                   com.sun.xml.internal.,/
                   com.sun.imageio.,/
                   com.sun.istack.internal.,/
                   com.sun.jmx.,/
                   com.sun.media.sound.,/
                   com.sun.naming.internal.,/
                   com.sun.proxy.,/

如果直接访问 package.accesspackage.definition 中的类会抛出 security exception ,除非这些定义的类是被允许的。

还有比如:

# Determines whether this properties file can be appended to
# or overridden on the command line via -Djava.security.properties
#
security.overridePropertiesFile=true

设置了 security.overridePropertiesFile=true 表示可以覆盖或者是扩展默认的策略的配置文件。(这一点在后面会进行说明)

还有类似于以 -Djava.security.policy=somefile 的方式载入自定义的策略文件:

# whether or not we allow an extra policy to be passed on the command line
# with -Djava.security.policy=somefile. Comment out this line to disable
# this feature.
policy.allowSystemProperty=true

其中还包括了策略文件的定义:

# 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

默认的策略文件的定义是在 ${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 {
        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";
};

分析 java.policy 文件

  1. "file:$/*" 使用了 permission java.security.AllPermission ,表示开启了所有的权限。
  2. permission java.lang.RuntimePermission "stopThread"; 表示运行调用进程的暂停方法。
  3. 系统属性的读取,如 permission java.util.PropertyPermission "java.version", "read"; 表示允许读取 java.version

我们以 permission java.util.PropertyPermission "java.version", "read"; 为例进行说明。编写测试文件

Test.java

public class Test {
    public static void main(String[] args) {
        System.out.println(System.getProperty("java.version"));
        System.setProperty("java.version","123");
        System.out.println(System.getProperty("java.version"));
    }
}

在不开启 Security Manager 的情况下运行 java Test 得到的结果是:

$ java Test
1.8.0_161
123

可以看到通过 System.setProperty("java.version","123"); ,我们改变了 java.version 的值。

开启 Security Manager 的情况下运行 java Test 得到的结果是:

$ java -Djava.security.manager Test
1.8.0_161
Exception in thread "main" java.security.AccessControlException: access denied ("java.util.PropertyPermission" "java.version" "write")
        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.System.setProperty(System.java:792)
        at Test.main(Test.java:4)

由于在默认的 java.policy 中仅仅只是设置允许读取 java.version ,所以尝试进行设置 java.version 就会抛出 AccessControlException 异常。

我们可以自定义我们的策略文件。例如我自定义的策略文件如下:

my.policy

grant {
    permission java.util.PropertyPermission "java.version", "read";
};

表示只会允许读取 java.version 信息。

Test.java

import java.io.IOException;

public class Test {
    public static void main(String[] args) throws IOException {
        System.out.println(System.getProperty("java.version"));
        System.setProperty("java.version","123");
        System.out.println(System.getProperty("java.version"));
    }
}

我们的策略文件的写法是:

my.policy

grant {
    permission java.util.PropertyPermission "java.version", "write";
};

使用 添加 模式,命令是 java -Djava.security.manager -Djava.security.policy=D:/my.policy (注意其中的 -Djava.security.policy=D:/my.policy 只有一个等号)。得到的结果如下:

$ java -Djava.security.manager -Djava.security.policy=D:/my.policy Test
1.8.0_161
123

如果我们变为 覆盖模式 ,命令是 java -Djava.security.manager -Djava.security.policy==D:/my.policy (注意其中的 -Djava.security.policy==D:/my.policy 只有两个等号)。得到的结果如下:

$ java -Djava.security.manager -Djava.security.policy==D:/my.policy Test
Exception in thread "main" java.security.AccessControlException: access denied ("java.util.PropertyPermission" "java.version" "read")
        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.SecurityManager.checkPropertyAccess(SecurityManager.java:1294)
        at java.lang.System.getProperty(System.java:717)
        at Test.main(Test.java:8)

可以看到直接报错,因为此时只为 java.version 设置了 write 权限没有读权限,所以 System.getProperty("java.version") 就会报错。

以上就是利用策略文件实现对于权限的访问控制。

参考

The Security Manager

Java安全管理器-SecurityManager

Permissions in the Java Development Kit (JDK)
原文  http://blog.spoock.com/2019/12/21/Getting-Started-with-Java-SecurityManager-from-Zero/
正文到此结束
Loading...