最近整理文档的时候,发现在角落里躺着几篇有关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.access
和 package.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
文件
"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
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)