说明
最近整理文档的时候,发现在角落里躺着几篇有关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
定义了很多与安全相关的配置。例如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.access
和package.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
文件
"file:$/*"
使用了permission java.security.AllPermission
,表示开启了所有的权限。permission java.lang.RuntimePermission "stopThread";
表示运行调用进程的暂停方法。- 系统属性的读取,如
permission java.util.PropertyPermission "java.version", "read";
表示允许读取java.version
。
我们以permission java.util.PropertyPermission "java.version", "read";
为例进行说明。编写测试文件
Test.java
1
2
3
4
5
6
7public 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
3grant {
permission java.util.PropertyPermission "java.version", "read";
};
表示只会允许读取java.version
信息。
Test.java
1
2
3
4
5
6
7
8
9import 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
3grant {
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)