透过AccessController深入了解Java安全模型

问题

上一期在使用到MappedByteBuffer时,采用的其中一种方式(AccessController)来释放已分配的内存映射,今天具体探讨一下AccessController的前世今生。

回顾上一期代码:

AccessController.doPrivileged(new PrivilegedAction() {
    public Object run() {
      try {
        Method getCleanerMethod = buffer.getClass().getMethod("cleaner", new Class[0]);
        getCleanerMethod.setAccessible(true);
        sun.misc.Cleaner cleaner = (sun.misc.Cleaner)
        getCleanerMethod.invoke(byteBuffer, new Object[0]);
        cleaner.clean();
      } catch (Exception e) {
        e.printStackTrace();
      }
      return null;
    }
});

首先我们先了解一个概念,在Java的设计中,实际上是有安全上的考虑,但是大家在开发过程中,很少接触这方面,也几乎用不到。所以关于这方面的材料也不多,网上找到的基本都是Java安全模型介绍

本文中采用的示例说明基本也摘抄自上述文章。

代码入手

我们先抛开所有概念,从代码入手来看看现在的Java安全模型是如何实现的。

1. 前提

两个项目,一个X项目,一个Y项目

X项目对于<font color=red>某个目录(目录属于X项目)</font>有写权限(该权限由Java安全模型控制,后续会讲到)

Y项目调用X项目中的写文件工具类,且Y项目没有授权<font color=red>某个目录</font>的写权限

2. 代码块

<b>X项目</b>(项目路径:D:\workspace\projectX\;代码路径在项目路径bin目录下;某个目录:D:\workspace\projectX\bin):

package learn.java.security;

import java.io.File;
import java.io.IOException;
import java.security.AccessControlException;
import java.security.AccessController;
import java.security.PrivilegedAction;

public class FileUtil {
   // 工程 A 执行文件的路径
   private final static String FOLDER_PATH = "D:\\workspace\\projectX\\bin";

   public static void makeFile(String fileName) {
       try {
           // 尝试在工程 A 执行文件的路径中创建一个新文件
           File fs = new File(FOLDER_PATH + "\\" + fileName);
           fs.createNewFile();
       } catch (AccessControlException e) {
           e.printStackTrace();
       } catch (IOException e) {
           e.printStackTrace();
       }
   }

   public static void doPrivilegedAction(final String fileName) {
       // 用特权访问方式创建文件
       AccessController.doPrivileged(new PrivilegedAction<String>() {
           @Override
           public String run() {
               makeFile(fileName);
               return null;
           }
       });
   }
}

<b>Y项目</b>(项目路径:D:\workspace\projectY\;代码路径在项目路径bin目录下)

package demo.security;

import java.io.File;
import java.io.IOException;
import java.security.AccessControlException;

import learn.java.security.FileUtil;

public class DemoDoPrivilege {

   public static void main(String[] args) {
       System.out.println("***************************************");
       System.out.println("I will show AccessControl functionality...");

       System.out.println("Preparation step : turn on system permission check...");
       // 打开系统安全权限检查开关
       System.setSecurityManager(new SecurityManager());
       System.out.println();

       System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
       System.out.println("
       Create a new file named temp1.txt via privileged action ...");
       // 用特权访问方式在工程 A 执行文件路径中创建 temp1.txt 文件
       FileUtil.doPrivilegedAction("temp1.txt");
       System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
       System.out.println();

       System.out.println("/////////////////////////////////////////");
       System.out.println("Create a new file named temp2.txt via File ...");
       try {
           // 用普通文件操作方式在工程 A 执行文件路径中创建 temp2.txt 文件
           File fs = new File(
                   "D:\\workspace\\projectX\\bin\\temp2.txt");
           fs.createNewFile();
       } catch (IOException e) {
           e.printStackTrace();
       } catch (AccessControlException e1) {
           e1.printStackTrace();
       }
       System.out.println("/////////////////////////////////////////");
       System.out.println();

       System.out.println("-----------------------------------------");
       System.out.println("create a new file named temp3.txt via FileUtil ...");
       // 直接调用普通接口方式在工程 A 执行文件路径中创建 temp3.txt 文件
       FileUtil.makeFile("temp3.txt");
       System.out.println("-----------------------------------------");
       System.out.println();

       System.out.println("***************************************");
   }
}

至此代码已完。现在开始对安全开始授权,假设有以下授权策略文件(MyPolicy.txt)放在Y工程根目录下:

// 授权项目X的Java执行文件在其某目录中的写文件权限
grant codebase "file:/D:/workspace/projectX/bin"
{
 permission java.io.FilePermission
   "D:\\workspace\\projectX\\bin\\*", "write";
};

即可指定安全文件运行程序:

java -Djava.security.policy=.\\MyPolicy.txt -classpath
D:\workspace\projectY\bin;D:\workspace\projectX\bin demo.security.DemoDoPrivilege

执行结果:

***************************************
I will show AccessControl functionality...
Preparation step : turn on system permission check...

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Create a new file named temp1.txt via privileged action ...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

////////////////////////////////////////
Create a new file named temp2.txt via File ...
java.security.AccessControlException: Access denied (java.io.FilePermission
     D:\workspace\projectX\bin\temp2.txt write)
    at java.security.AccessController.checkPermission(AccessController.java:108)
    at java.lang.SecurityManager.checkPermission(SecurityManager.java:533)
    at java.lang.SecurityManager.checkWrite(SecurityManager.java:963)
    at java.io.File.createNewFile(File.java:882)
    at demo.security.DemoDoPrivilege.main(DemoDoPrivilege.java:32)
////////////////////////////////////////

----------------------------------------
create a new file named temp3.txt via FileUtil ...
java.security.AccessControlException: Access denied (java.io.FilePermission
    D:\workspace\projectX\bin\temp3.txt write)
    at java.security.AccessController.checkPermission(AccessController.java:108)
    at java.lang.SecurityManager.checkPermission(SecurityManager.java:533)
    at java.lang.SecurityManager.checkWrite(SecurityManager.java:963)
    at java.io.File.createNewFile(File.java:882)
    at learn.java.security.FileUtil.makeFile(FileUtil.java:16)
    at demo.security.DemoDoPrivilege.main(DemoDoPrivilege.java:43)
----------------------------------------

***************************************

从执行结果可以看出,当安全模式生效时,Y项目通过普通接口(调用已有权限的代码)、自己创建的方式都无法在没有权限的目录下操作。只有通过特权访问(AccessController)的方式才成功。

可以看出,要想访问安全资源,要么在调用链上权限齐全(即自己有对应的权限),要么就有特权访问。

一直在说特权访问,从实现上看已大概了解特权访问了,那实际上又是什么呢?

3. 特权访问

在Java中执行程序分为本地和远程两种,本地代码默认都是可信任的,而远程代码被看作不受信的。在以前的Java版本中,不授信的远程代码基于沙箱机制,只能访问限定在JVM特定的运行范围中,并严格控制代码对本地资源的访问。而本地代码可以访问一切本地资源。有效隔离远程代码,防止对本地系统造成破坏。

关于Java安全机制的发展史可参考上面关于安全机制的链接。

发展到现在,安全模型引入了域的概念。将代码加载到不同的系统域和应用域,系统域负责与关键资源交互,各应用域通过系统域的代理对各种需要的资源进行访问(如上述Y项目通过X项目对资源进行写入)。虚拟机不同的受保护域,对应不一样的权限(permission)。存在域中的类文件就有了当前域的全部权限(如上述X项目有X域的权限而Y项目没有)


Java最新安全模型.png

看过整个的安全模型,在用法上,最常用就是上述的API(doPrivileged),能使一段受信代码获得最大权限,甚至比调用他的应用程序还多(如Y调用X,明显比Y权限多),可临时访问更多资源。

主要用于一些特殊应用场景:应用无法直接访问某些系统资源,但应用又必须得到这些资源才能完成功能。doPrivileged让程序突破当前域权限限制,临时扩大访问权限。

AccessController的工作机制:

在某个线程调用栈中,当AccessController的checkPermission方法被最近调用程序(如上述代码创建文件时)调用时,对于程序要求的访问权限,ACC决定是否授权算法如下:

  1. 如果调用链中某个调用程序没有需要的权限,抛出AccessControlException(如上述代码Y无权限)
  2. 满足以下情况即被授权
  • 调用程序访问另一个有该权限的方法,且此方法标记为有访问特权(如上述代码Y调用X的特权方法)
  • 调用程序调用的(直接或间接)后续对象有权限(如上述代码X有权限)

回归问题本质

我们mmap也没有使用外部远程代码,何必用AccessController这么复杂,直接把AccessController去掉试一下。实际上看貌似也是没问题的。那为什么那么多的例子都是用AccessController来释放资源呢?

但由于Cleaner是在rt.jar包中,所以有可能会出现没有权限访问的情况,jvm把其加入不同域中。暂时来看只是猜测。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,326评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,228评论 1 304
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,979评论 0 252
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,489评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,894评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,900评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,075评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,803评论 0 205
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,565评论 1 249
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,778评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,255评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,582评论 3 261
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,254评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,151评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,952评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,035评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,839评论 2 277