5

Java SecurityManager初识

 3 years ago
source link: https://blog.spoock.com/2019/12/21/Getting-Started-with-Java-SecurityManager-from-Zero/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Java SecurityManager初识

2019-12-21

最近整理文档的时候,发现在角落里躺着几篇有关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以及定义操作。这两种方式都会在本文中进行说明。

  1. 自定义SecurityManager策略文件
  2. 继承SecurityManager,通过代码的方式,对每个操作进行控制。

自定义SecurityManager策略文件

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 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,除非这些定义的类是被允许的。

还有比如:

1
2
3
4
# 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的方式载入自定义的策略文件:

1
2
3
4
# 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

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

1
2
3
4
# 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。内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 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

1
2
3
4
5
6
7
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得到的结果是:

1
2
3
$ java Test
1.8.0_161
123

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

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

1
2
3
4
5
6
7
8
$ 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

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

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

Test.java

1
2
3
4
5
6
7
8
9
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

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

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

1
2
3
$ 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只有两个等号)。得到的结果如下:

1
2
3
4
5
6
7
8
$ 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)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK