zoukankan      html  css  js  c++  java
  • java安全沙箱(四)之安全管理器及Java API

    java是一种类型安全的语言,它有四类称为安全沙箱机制的安全机制来保证语言的安全性,这四类安全沙箱分别是:

    本篇博客主要介绍“类安全管理器及Java API”的基本原理,如需了解其它几类安全机制可以通过上面的博客链接进入查看。

    简介

    java安全沙箱的前三类保证了jvm所运行程序的完整性,使得jvm不会因为运行有漏洞或恶意的代码而导致出现不可预期的状态。而第四类沙箱模型是“类安全管理器及Java API”,它能保护jvm在运行有漏洞或恶意的代码不会破坏外部资源。java通过称为安全管理器的一类API来保证这类安全性。

    安全策略文件

    首先介绍下安全策略文件,如果启用了安全管理器,默认会使用jre自带的安全策略文件$JAVA_HOME/jre/lib/security/java.policy来指定访问外部资源的权限,该策略文件也可以通过jvm参数-Djava.security.policy来指定。

    Policy文件的主要格式如下:

    keystore "some_keystore_url", "keystore_type";
    grant [SignedBy "signer_names"] [, CodeBase "URL"] {
        Permission permission_class_name ["target_name"] [,"action"] [,SignedBy"signer_names"];
        … …
    };
    1. "keystore"记录 

      一个keystore是一个私有密钥(private keys)数据库和相应的数字签名,例如X.509证书。Policy文件中可能只有一条keystore记录(也可能不含有该记录),它可以出现在文件中grant记录以外的任何地方。Policy配置文件中指定的keystores用于寻找grant记录中指定的、签名者的公共密钥(public keys),如果任何grant记录指定签名者(signer_names),那么,keystore记录必须出现在policy配置文件中。

      "some_keystore_url"是指keystore的URL位置,"keystore_type"是指keystore的类型。第二个选项是可选项,如果没有指定,该类型则假定由安全属性文件(java.security)中的"keystore.type"属性来确定。keystore类型定义了keystore信息的存储和数据格式,用于保护keystore中的私有密钥和keystore完整性的算法。Sun Microsystems支持的缺省类型为“JKS”。

    2. "grant"记录

      在Policy文件中的每一个grant记录含有一个CodeSource(指定代码)及其permission(许可)。Policy文件中的每一条grant记录遵循下面的格式,以保留字“grant”开头,表示一条新的记录的开始,“Permission”是另一个保留字,在记录中用来标记一个新的许可的开始。每一个grant记录授予一个指定的代码(CodeBase)一套许可(Permissions)。permission_class_name必须是一个合格并存在的全限定性类名,例如java.io.FilePermission。

      target_name用来指定目标类的位置,action用于指定目标类拥有的权限。 target_name可以直接指定类名(可以是绝对或相对路径),目录名,也可以使用通配符/、/*或着/-。

      directory/ 表示directory目录下的所有.class文件,不包括.jar文件

      directory/* 表示directory目录下的所有的.class及.jar文件

      directory/- 表示directory目录下的所有的.class及.jar文件,包括子目录

    下面是一个policy文件的demo:

    grant codeBase "file:${{java.ext.dirs}}/*" {
            permission java.security.AllPermission;
    };
    grant {
            permission java.lang.RuntimePermission "stopThread";
            permission java.net.SocketPermission "localhost:1099", "listen";        
            permission java.util.PropertyPermission "java.version", "read";
            ... ...
    };

    例如:对于java.net.SocketPermission,action可以是:listen,accept,connect,read,write;对于java.io.FilePermission,action可以是:read, write, delete和execute。

    安全管理器

    java的安全管理器可以定制,也可以使用jdk的默认实现java.lang.SecurityManager,启动安全管理器的话有两种方式,一种是通过硬编码的方式启动,另外一种是通过jvm参数-Djava.security.manager启动。

    下面的测试用例都使用jre的默认policy文件配置:

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

    该策略文件指定了"java.version"的读权限,然后并没有指定写权限。参考以下测试用例:

    public static void main(String... args) {
        String javaVersion=System.getProperty("java.version");
        System.err.println(javaVersion);
        System.setProperty("java.version","1.7.0_45");
        String javaNewVersion=System.getProperty("java.version");
        System.err.println(javaNewVersion);
      }

    首先读"java.version"属性,然后把该属性改写为1.7.0_45,最后再读取它并打印出来,输出结果为:

    1.8.0_45
    1.7.0_45

    可以看到默认的jdk版本为1.8.0_45(1.8.0是java的主版本号,45是次版本号)。

    然后前面的policy文件只指定了read权限,为什么这里却write成功了?那是因为默认情况下java并不启动安全管理器,可以使用硬编码System.setSecurityManager()来启动安全管理器,参考以下测试用例:

    public static void main(String... args) {
        // 启用安全管理器
        System.setSecurityManager(new SecurityManager());
        String javaVersion=System.getProperty("java.version");
        System.err.println(javaVersion);
        System.setProperty("java.version","1.7.0_45");
        String javaNewVersion=System.getProperty("java.version");
        System.err.println(javaNewVersion);
      }

    此时的输出结果为:

    1.8.0_45
    Exception in thread "main" java.security.AccessControlException: access denied ("java.util.PropertyPermission" "java.version" "write")
        at java.security.AccessControlContext.checkPermission(AccessControlContext.java:457)
        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.Test.main(Test.java:9)

    结果很明确:可以读,但不能写,我们可以来修改policy文件,让它支持"java.version"的写操作:

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

    此时再执行上面的用例,输出结果为:

    1.8.0_45
    1.7.0_45

    可以看到此时可以支持"java.version"的写操作了。

    另外使用jvm参数-Djava.security.manager也能启用安全管理器,此时jvm启动时会设置"java.security.manager"系统属性为空字符串"",此时会在启动sun.misc.Launcher时初始化安全管理器,查看sun.misc.Launcher的源码:

    public Launcher(){
            ExtClassLoader extclassloader;
            try {
                extclassloader = ExtClassLoader.getExtClassLoader();
            } catch(IOException ioexception) {
                throw new InternalError("Could not create extension class loader", ioexception);
            }
            try {
                loader = AppClassLoader.getAppClassLoader(extclassloader);
            } catch(IOException ioexception1) {
                throw new InternalError("Could not create application class loader", ioexception1);
            }
            Thread.currentThread().setContextClassLoader(loader);
            String s = System.getProperty("java.security.manager");
            if(s != null) {
                SecurityManager securitymanager = null;
                if("".equals(s) || "default".equals(s))
                    securitymanager = new SecurityManager();
                else
                    try {
                        securitymanager = (SecurityManager)loader.loadClass(s).newInstance();
                    }
                    catch(IllegalAccessException illegalaccessexception) { }
                    catch(InstantiationException instantiationexception) { }
                    catch(ClassNotFoundException classnotfoundexception) { }
                    catch(ClassCastException classcastexception) { }
                if(securitymanager != null)
                    System.setSecurityManager(securitymanager);
                else
                    throw new InternalError((new StringBuilder()).append("Could not create SecurityManager: ").append(s).toString());
            }
        }

    可以看到"java.security.manager"系统属性为空字符串""时会启用jdk的默认安全管理器SecurityManager。

    Java API

    java的安全机制api大部分都在java.security包下,因为源码很多,就不贴出来了,大家感兴趣的话可以研究下。以下是一些常用的类的api介绍:

    • java.security.AccessControlContext:基于它所封装的上下文作出系统资源访问决定,该类最常用于将代码标记为享有“特权”。

    • java.security.AccessController:用于与访问控制相关的操作和决定。java.security.SecureClassLoader此类扩展了 ClassLoader,支持使用相关的代码源和权限定义类,这些代码源和权限默认情况下可根据系统策略获取到。

    • java.security.Provider:此类表示 Java 安全 API "provider",这里 provider 实现了 Java 安全性的一部分或者全部。

    • java.security.Permission:表示访问系统资源的抽象类。所有权限都有一个名称(对它们的解释依赖于子类),以及用来定义特定 Permission 子类的语义的抽象方法。

  • 相关阅读:
    vue-fullcalendar插件
    iframe 父框架调用子框架的函数
    关于调试的一点感想
    hdfs 删除和新增节点
    hadoop yarn 实战错误汇总
    Ganglia 安装 No package 'ck' found
    storm on yarn(CDH5) 部署笔记
    spark on yarn 安装笔记
    storm on yarn安装时 提交到yarn失败 failed
    yarn storm spark
  • 原文地址:https://www.cnblogs.com/duanxz/p/6108357.html
Copyright © 2011-2022 走看看