java安全管理器作用 java系统安全

java安全模型

Java 安全模型,你了解了吗

在Java中将执行程序分为本地和远程两种,本地代码默认视为可信任的,而远程代码则被看作是不受信的。对于受信的本地代码,可以访问一切本地资源。而对于非受信的远程代码在早期的Java实现中,安全依赖于沙箱(Sandbox)机制。沙箱机制就是将Java代码限定在虚拟机(JVM)特定的运行范围中,并且严格限制代码对本地系统的资源访问,通过这样的措施来保证对远程代码的有效隔离,防止对本地系统造成破坏,如图1所示,

但如此严格的安全机制也给程序的功能扩展带来了障碍,比如当用户希望远程代码访问本地系统的文件时候,就无法实现。因此在后续的Java1.1版本中,针对安全机制做了改进,增进了安全策略,允许用户指定代码对本地资源的访问权限,如图2所示,

在java2.1版本中,再次改进了安全机制,增加了代码签名。不论本地代码或是远程代码,都会按照用户的安全策略设定,由类加载到虚拟机中权限不同的运行空间,来实现差异化的代码权限控制。

当前最新的安全机制实现,则引入了域(Domain)的概念。虚拟机会把所有代码加载到不同的系统域和应用域,系统域部分专门负责域关键资源进行交互,而各个应用域部分则通过系统与部分代理来对各种需要的资源进行访问。虚拟机中不同的受保护域(Protected Domain),对应不一样的权限。存在于不同域的类文件就有了当前域的全部权限

理解java沙箱

什么是沙箱

Java安全模型的核心就是Java沙箱(sandbox),什么是沙箱?沙箱是一个限制程序运行的环境。限制程序运行一方面是为了保护系统资源,同时另一方面也为了保护程序自己。沙箱主要限制系统资源访问,那系统资源包括什么?---CPU、内存、文件系统、网络。不同级别的沙箱对这些资源访问的限制也可以不一样。

所有的Java程序运行都可以指定沙箱,可以定制安全策略。而一个Java程序运行的安全策略,包括了以下几点基础:

  • 字节码校验器(bytecode verifier):确保Java类文件遵循Java语言规范。这样可以帮助Java程序实现内存保护。但是不是所有的类文件都会经过字节码校验,比如核心类。
  • 类加载器(class loader):所有的java类都是通过类加载器加载的,可以自定义类加载器来设置加载类的权限。
  • 存取控制器(access controller):存取控制器可以控制核心API对操作系统的存取权限,而这个控制的策略设定,可以由用户指定。
  • 安全管理器(security manager):是核心API和操作系统之间的主要接口。实现权限控制,比存取控制器优先级高。
  • 安全软件包(security package):java.security下的类和扩展包下的类,允许用户为自己的应用增加新的安全特性,包括:

    • 安全提供者
    • 消息摘要
    • 数字签名
    • 加密

沙箱包含要素

权限

权限是指允许代码执行的操作。包含三部分:权限类型、权限名和允许的操作。权限名一般就是对哪类资源进行操作的资源定位(比如说一个文件名或者通配符、网络主机等),一般基于权限类型来设置,有的比如java.security.AllPermission不需要权限名。允许的操作也和权限类型对应,指定了都目标可以执行的操作行为,比如读、写等。如下面的例子:

permission java.security.AllPermission; //权限类型
permission java.lang.RuntimePermission "StopThread"; //权限类型+权限名
permission java.io.FilePermission "/tmp/foo" "read";    //权限类型+权限名+允许的操作

标注权限

  • 文件权限
  • 套接字权限
  • 属性权限:jvm属性名
  • 运行时权限
  • AWT权限
  • 网络权限
  • 安全权限
  • 序列化权限
  • 反射权限
  • 完全权限
  • 。。。。

代码源

代码源是类所在的位置,表示为以URL为地址

保护域

保护域用来组合代码源和权限,这是沙箱的基本概念。保护域就在于声明了比如由代码A可以做权限B这样的事情。

策略文件

策略文件在控制沙箱的管理元素,一个策略文件包含一个或多个保护域的项。策略文件完成了代码权限的指定任务,策略文件包含全局和用户专属两种。

为了管理沙箱,策略文件我认为是最重要的内容。JVM可以使用多个策略文件,不过一般两个最常用。一个是全局的:$JREHOME/lib/security/java.policy,作用域JVM的所有实例。另一个是用户自己的,可以存储到用户的主目录下。策略文件可以使用jdk自带的policytool工具编辑。

java安全管理器作用 java系统安全
https://blog.51cto.com/u_16099261/6524283

SecurityManager

当运行未知的Java程序的时候,该程序可能有恶意代码(删除系统文件、重启系统等),为了防止运行恶意代码对系统产生影响,需要对运行的代码的权限进行控制,这时候就要启用Java安全管理器。

配置文件

 默认的安全管理器配置文件是 $JAVA_HOME/jre/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 { 
    // Allows any thread to stop itself using the java.lang.Thread.stop()
    // method that takes no argument.
    // Note that this permission is granted by default only to remain
    // backwards compatible.
    // It is strongly recommended that you either remove this permission
    // from this policy file or further restrict it to code sources
    // that you specify, because Thread.stop() is potentially unsafe.
    // See the API specification of java.lang.Thread.stop() for more
        // information.
    permission java.lang.RuntimePermission "stopThread";

    // allows anyone to listen on un-privileged ports
    permission java.net.SocketPermission "localhost:1024-", "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";
};

启动安全管理器

启动程序的时候通过附加参数启动安全管理器:

-Djava.security.manager

  若要同时指定配置文件的位置那么示例如下:

-Djava.security.manager -Djava.security.policy="E:/java.policy"

  也可以通过编码方式启动,不过不建议:

System.setSecurityManager(new SecurityManager());

  通过参数启动,本质上也是通过编码启动,不过参数启动使用灵活,项目启动源码如下(sun.misc.Launcher):

// Finally, install a security manager if requested
String s = System.getProperty("java.security.manager");
if (s != null) {
    SecurityManager sm = null;
    if ("".equals(s) || "default".equals(s)) {
        sm = new java.lang.SecurityManager();
    } else {
        try {
            sm = (SecurityManager)loader.loadClass(s).newInstance();
        } catch (IllegalAccessException e) {
        } catch (InstantiationException e) {
        } catch (ClassNotFoundException e) {
        } catch (ClassCastException e) {
        }
    }
    if (sm != null) {
        System.setSecurityManager(sm);
    } else {
        throw new InternalError(
            "Could not create SecurityManager: " + s);
    }
}

可以发现将会创建一个默认的SecurityManager;

配置文件简单解释

配置文件规则

在启用安全管理器的时候,配置遵循以下基本原则:

  1. 没有配置的权限表示没有。
  2. 只能配置有什么权限,不能配置禁止做什么。
  3. 同一种权限可多次配置,取并集。
  4. 统一资源的多种权限可用逗号分割。
grant codeBase "file:${{java.ext.dirs}}/*" {
    permission java.security.AllPermission;
};

  授权基于路径在"file:${{java.ext.dirs}}/*"的class和jar包,所有权限。

  第二部分授权:

grant { 
    permission java.lang.RuntimePermission "stopThread";
    ……   
}

这是细粒度的授权,对某些资源的操作进行授权。具体不再解释,可以查看javadoc。如RuntimePermission的可授权操作经查看javadoc如下:

权限目标名称权限所允许的操作允许此权限所带来的风险
createClassLoader创建类加载器授予该权限极其危险。能够实例化自己的类加载器的恶意应用程序可能会在系统中装载自己的恶意类。这些新加载的类可能被类加载器置于任意保护域中,从而自动将该域的权限授予这些类。
getClassLoader类加载器的获取(即调用类的类加载器)这将授予攻击者得到具体类的加载器的权限。这很危险,由于攻击者能够访问类的类加载器,所以攻击者能够加载其他可用于该类加载器的类。通常攻击者不具备这些类的访问权限。
setContextClassLoader线程使用的上下文类加载器的设置在需要查找可能不存在于系统类加载器中的资源时,系统代码和扩展部分会使用上下文类加载器。授予 setContextClassLoader 权限将允许代码改变特定线程(包括系统线程)使用的上下文类加载器。
enableContextClassLoaderOverride线程上下文类加载器方法的子类实现在需要查找可能不存在于系统类加载器中的资源时,系统代码和扩展部分会使用上下文类加载器。授予 enableContextClassLoaderOverride 权限将允许线程的子类重写某些方法,这些方法用于得到或设置特定线程的上下文类加载器。
setSecurityManager设置安全管理器(可能会替换现有的)安全管理器是允许应用程序实现安全策略的类。授予 setSecurityManager 权限将通过安装一个不同的、可能限制更少的安全管理器,来允许代码改变所用的安全管理器,因此可跳过原有安全管理器所强制执行的某些检查。
createSecurityManager创建新的安全管理器授予代码对受保护的、敏感方法的访问权,可能会泄露有关其他类或执行堆栈的信息。
getenv.{variable name}读取指定环境变量的值此权限允许代码读取特定环境变量的值或确定它是否存在。如果该变量含有机密数据,则这项授权是很危险的。
exitVM.{exit status}暂停带有指定退出状态的 Java 虚拟机此权限允许攻击者通过自动强制暂停虚拟机来发起一次拒绝服务攻击。注意:自动为那些从应用程序类路径加载的全部代码授予 "exitVM." 权限,从而使这些应用程序能够自行中止。此外,"exitVM" 权限等于 "exitVM."。
shutdownHooks虚拟机关闭钩子 (hook) 的注册与取消此权限允许攻击者注册一个妨碍虚拟机正常关闭的恶意关闭钩子 (hook)。
setFactory设置由 ServerSocket 或 Socket 使用的套接字工厂,或 URL 使用的流处理程序工厂此权限允许代码设置套接字、服务器套接字、流处理程序或 RMI 套接字工厂的实际实现。攻击者可能设置错误的实现,从而破坏数据流。
setIOSystem.out、System.in 和 System.err 的设置此权限允许改变标准系统流的值。攻击者可以改变 System.in 来监视和窃取用户输入,或将 System.err 设置为 "null" OutputStream,从而隐藏发送到 System.err 的所有错误信息。
modifyThread修改线程,例如通过调用线程的 interruptstopsuspendresumesetDaemonsetPrioritysetNamesetUncaughtExceptionHandler 方法此权限允许攻击者修改系统中任意线程的行为。
stopThread通过调用线程的 stop 方法停止线程如果系统已授予代码访问该线程的权限,则此权限允许代码停止系统中的任何线程。此权限会造成一定的危险,因为该代码可能通过中止现有的线程来破坏系统。
modifyThreadGroup修改线程组,例如通过调用 ThreadGroup 的 destroygetParentresumesetDaemonsetMaxPrioritystopsuspend 方法此权限允许攻击者创建线程组并设置它们的运行优先级。
getProtectionDomain获取类的 ProtectionDomain此权限允许代码获得特定代码源的安全策略信息。虽然获得安全策略信息并不足以危及系统安全,但这确实会给攻击者提供了能够更好地定位攻击目标的其他信息,例如本地文件名称等。
getFileSystemAttributes获取文件系统属性此权限允许代码获得文件系统信息(如调用者可用的磁盘使用量或磁盘空间)。这存在潜在危险,因为它泄露了关于系统硬件配置的信息以及一些关于调用者写入文件特权的信息。
readFileDescriptor读取文件描述符此权限允许代码读取与文件描述符读取相关的特定文件。如果该文件包含机密数据,则此操作非常危险。
writeFileDescriptor写入文件描述符此权限允许代码写入与描述符相关的特定文件。此权限很危险,因为它可能允许恶意代码传播病毒,或者至少也会填满整个磁盘。
loadLibrary.{库名}动态链接指定的库允许 applet 具有加载本机代码库的权限是危险的,因为 Java 安全架构并未设计成可以防止恶意行为,并且也无法在本机代码的级别上防止恶意行为。
accessClassInPackage.{包名}当类加载器调用 SecurityManager 的checkPackageAccess 方法时,通过类加载器的 loadClass 方法访问指定的包此权限允许代码访问它们通常无法访问的那些包中的类。恶意代码可能利用这些类帮助它们实现破坏系统安全的企图。
defineClassInPackage.{包名}当类加载器调用 SecurityManager 的 checkPackageDefinition 方法时,通过类加载器的 defineClass 方法定义指定的包中的类。此权限允许代码在特定包中定义类。这样做很危险,因为具有此权限的恶意代码可能在受信任的包中定义恶意类,比如 java.securityjava.lang
accessDeclaredMembers访问类的已声明成员此权限允许代码查询类的公共、受保护、默认(包)访问和私有的字段和/或方法。尽管代码可以访问私有和受保护字段和方法名称,但它不能访问私有/受保护字段数据并且不能调用任何私有方法。此外,恶意代码可能使用该信息来更好地定位攻击目标。而且,它可以调用类中的任意公共方法和/或访问公共字段。如果代码不能用这些方法和字段将对象强制转换为类/接口,那么它通常无法调用这些方法和/或访问该字段,而这可能很危险。
queuePrintJob打印作业请求的开始这可能向打印机输出敏感信息,或者只是浪费纸张。
getStackTrace获取另一个线程的堆栈追踪信息。此权限允许获取另一个线程的堆栈追踪信息。此操作可能允许执行恶意代码监视线程并发现应用程序中的弱点。
setDefaultUncaughtExceptionHandler在线程由于未捕获的异常而突然终止时,设置将要使用的默认处理程序此权限允许攻击者注册恶意的未捕获异常处理程序,可能会妨碍线程的终止
Preferences表示得到 java.util.prefs.Preferences 的访问权所需的权限。java.util.prefs.Preferences 实现了用户或系统的根,这反过来又允许获取或更新 Preferences 持久内部存储中的操作。如果运行此代码的用户具有足够的读/写内部存储的 OS 特权,则此权限就允许用户读/写优先级内部存储。实际的内部存储可能位于传统的文件系统目录中或注册表中,这取决于平台 OS。

当批量配置的时候,有三种模式:

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

  可以通过${}来引用系统属性,如:

"file:${{java.ext.dirs}}/*"

问题解决

当出现关于安全管理的报错的时候,基本有两种方式来解决。

一般情况下都是无意启动安全管理器,所以这时候只需要把安全管理器进行关闭,去掉启动参数即可。

若因为没有权限报错,则报错信息中会有请求的权限和请求什么权限,如下:

Exception in thread "main" java.security.AccessControlException: access denied (java.io.FilePermission E:\pack\a\a.txt write)

上面例子,请求资源E:packaa.txt,的FilePermission的写权限没有,因此被拒绝。

也可以开放所有权限:

grant { 
    permission java.security.AllPermission;
};
Last modification:November 13, 2023
如果觉得我的文章对你有用,请随意赞赏