2. Java的安全模型

Java安全模型侧重于保护终端用户免受从网络下载的、来自不可靠来源的、恶意程序(以及善意程序中的bug)的侵犯。为了达到这个目的,Java提供了一个用户可配置的“沙箱”,在沙箱中可以放置不可靠的Java程序。

2.1 基本沙箱

沙箱模型使你可以接收来自任何来源的代码,而不是要求用户避免将来自不受信站点的代码下载到机器上。

组成Java沙箱的基本组件如下:

  • 类装载器结构
  • class文件检验器
  • 内置于Java虚拟机(及语言)的安全特性
  • 安全管理器及Java API

2.2 类装载器体系结构

在Java沙箱中,类装载器是第一道防线:

  • 它防止恶意代码去干涉善意的代码
  • 它守护了被信任的类库的边界
  • 它将代码归入某类(称为保护域),该类确定了代码可以进行哪些操作

类装载器体系结构可以防止恶意的代码去干涉善意的代码,是通过为由不同的类装载器装入的类提供不同的命名空间来实现的。命名空间由一系列唯一的名称组成,每一个被装载的类有一个名字,这个命名空间是由Java虚拟机为每一个类装载器维护的。

图2-1 类装载器和命名空间.png

类装载器体系结构守护了被信任的类库的边界,是通过分别使用不同的类装载器装载可靠的包和不可靠的包来实现的。

从Java 1.2版本开始,类装载器请求另一个用户自定义类装载器来装载一个类型的过程被形式化,称为双亲委派模式。除启动类装载器外的每一个类装载器,都有一个“双亲”类装载器,在某个特定的类装载器试图以常用方式装载类型之前,它会先默认地将这个任务“委派”给它的双亲——请求它的双亲来装载这个类型。

运行时包,是指由同一个类装载器装载的、属于同一个包的、多个类型的集合。在允许两个类型之间对包内可见的成员进行访问前,虚拟机不但要确定这两个类型属于同一个包,还必须确认它们属于同一个运行时包——它们必须是由同一个类装载器装载的。

除了屏蔽不同命名空间的类以及保护受信任的类库的边界外,类装载器还起到了另外的安全作用。类装载器必须将每一个被装载的类放置在一个保护域中,一个保护域定义了这个代码在运行时将得到怎样的权限。

2.3 class文件检验器

和类装载器一起,class文件检验器保证装载的class文件内容有正确的内部结构,并且这些class文件相互间协调一致。如果class文件检验器在class文件中发现了问题,它会抛出异常。

JVM的class文件检验器只在字节码执行之前进行一次分析,需要进行四趟独立的扫描:

  • 第一趟是在类被装载时进行的,主要检查class文件的内部结构
  • 第二趟和第三趟是在连接过程中进行的,主要确认类型数据遵从Java编程语言的语义,包括检验它所包含的所有字节码的完整性。
  • 第四趟是在动态连接的过程中解析符号时进行的,主要确认被引用的类、字段以及方法确实存在。

2.4 JVM中内置的安全特性

JVM在执行字节码时还进行其他一些内置的安全机制的操作,包括:

  • 类型安全的引用转换
  • 结构化的内存访问(无指针算法)
  • 自动垃圾收集(不必显式地释放被分配的内存)
  • 数组边界检查
  • 空引用检查

2.5 安全管理器和Java API

Java安全模型的前三个组成部分——类装载器体系结构、class文件检验器以及Java中内置的安全特性——一起达到一个共同的目的:保持JVM的实例和它正在运行的应用程序的内部完整性,使得它们不被下载的恶意或有漏洞的代码侵犯。相反,这个安全模型的第四个组成部分是安全管理器,它主要用于保护虚拟机的外部资源不被虚拟机内运行的恶意或有漏洞的代码侵犯。

安全管理器定义了沙箱的外部边界。因为它是可定制的,所以它允许为程序建立自定义的安全策略。

Java应用程序在启动时不会默认安装安全管理器,也不会有任何安全限制。在Java 1.2版本以前,如果安装了安全管理器,那么它将负责应用程序整个剩余的生命周期,不能被替代、扩展或者修改。在1.2版本中,当前安装的安全管理器能够在允许的情况下被替换。

安全管理器主要负责两个方面的工作:说明一个安全策略以及执行这个安全策略。

Java 1.2版本中的java.lang.SecurityManager是一个具体的类,它提供了一个默认的安全管理器的实现,能够辅助建立一个基于代码签名的细粒度的安全策略。用户可以显示实例化这个安全管理器,或者让它自动安装。例如,在Sun的Java 2 SDK版本1.2中,可以在命令行使用-Djava.security.manager选项来指明安装具体安全管理器。

具体安全管理器类允许用户不用Java代码定义自己的安全策略,而是用一个称为策略文件的ASCII文件。在策略文件中,可以给代码来源授予权限。权限是用类定义的,它是java.security.Permission的子类。代码来源是由代码库的URL和一些签名组成的,从这个URL可以装载代码,而签名则为这个代码作担保。当创建安全管理器时,它对策略文件进行解析,并创建CodeSource(代码来源)和Permission(权限)对象,这些对象被封装在一个单独的Policy对象中,这个Policy对象就代表了运行时的策略。任何时刻只能有一个Policy对象被安装。

类装载器将类型放到保护域(ProtectionDomain)中,保护域封装了授予代码来源的所有权限,这些代码来源由装载的类型代表。

当具体安全管理器的check方法被调用时,它们中的大多数都将请求传递给一个称为AcessController的类。这个AccessController使用了包含在保护域对象中的信息,这个对象所属的类的方法在调用栈中,AccessController进行栈检查以确定这个操作能否被执行。

java.lang.SecurityManager中有两个关键方法:

  • checkPermission(Permission perm) —— 进行某个操作(它需要指定的权限)前被调用
  • checkPermission(Permission perm, Object context) —— 在被传递的安全上下文中进行某个操作(它需要指定的权限)前被调用

在具体安全管理器类中,checkPermission( )方法同样负责决定,是否允许将某个操作的任务委派给另一个方法。这个checkPermission( )方法只是简单地调用了类java.security.AccessController中的静态checkPermission( )方法,并将这个Permission对象传递给它。

2.6 代码签名和认证

Java安全模型很重要的一点就是它支持认证。认证可以使用户确认,由某些团体担保的一些class文件是值得信任的,并且这些class文件在到达用户虚拟机的途中没有被改变。这样,如果用户在一定程度上信任这个为代码作担保的团体,也就可以在一定程度上简化沙箱对这段代码的限制。可以对由不同团体签名的代码建立不同的安全限制。

图2-2 对一个jar文件签名.png
图2-3 对一个已签名的jar文件验签.png

2.7 策略

Java安全体系结构的真正好处在于,它可以对代码授予不同层次的信任度来部分地访问系统。

版本1.2的安全体系结构的主要目标之一就是使建立(以代码签名为基础的)细粒度的访问控制策略的过程更为简单且更少出错。在版本1.2的安全模型中,权限(系统访问权限)使授予代码来源的。

对应于整个Java应用程序的一个访问控制策略是由抽象类java.security.Policy的一个子类的单个实例所表示的。在任何时候,每个应用程序实际上都只有一个Policy对象。类装在其利用这个Policy对象来帮助它们决定,在把一段代码导入虚拟机时应该给它们怎么样的权限。

安全策略是一个从描述运行代码的属性集合到这段代码所拥有的权限的映射。在版本1.2的安全体系结构中,描述运行代码的属性被总称为代码来源

在版本1.2中,所有和具体安全管理器有关的工具和访问控制体系结构都只对证书起作用,而不能对“赤裸”的公钥起作用。

权限是由抽象类java.security.Permission的一个子类的实例表示的。一个Permission对象有三个属性:类型、名字和可选的操作。

在Policy对象中,每一个CodeSource是和一个或多个Permission对象相关联的。

策略文件

由Sun提供的在Java 1.2平台下的具体Policy子类采用如下方法:在一个ASCII策略文件中用上下文无关文法描述安全策略。

keystore "ijvmkeys";

grant signedBy "friend" {
    permission java.io.FilePermission "question.txt", "read";
    permission java.io.FilePermission "answer.txt", "read";
};

grant signedBy "stranger" {
    permission java.io.FilePermission "question.txt", "read";
};

grant codeBase "file:${com.artima.ijvm.cdrom.home}/security/ex2/*" {
    permission java.io.FilePermission "question.txt", "read";
    permission java.io.FilePermission "answer.txt", "read";
};

最后一条grant语句中的代码库URL采用了文件的形式:它包含了一个属性${com.artima.ijvm.cdrom.home}

2.8 保护域

当类装载器将类型装入JVM时,它们将为每个类型指派一个保护域。保护域定义了授予一段特定代码的所有权限。(一个保护域对应于策略文件中的一个或多个grant子句。)装载入JVM的每一个类型都属于且仅属于一个保护域。

虽然前面的Policy对象代表了一个从代码来源到权限的全局映射,但是最终还是由类装载器负责决定代码执行时将获得怎样的权限。

图2-4用图形化的方式描述了保护域、代码来源以及权限。ProtectionDomain对象封装了一个到CodeSource对象的引用以及一个到java.security.Permissions对象的引用。java.security.Permissions是抽象类java.security.PermissionCollection的一个具体类,代表了一个同构权限的集合。

图2-4 保护域、代码来源以及权限.png

当一个类装载器将Friend和Friend$1导入方法区时,类装载器将把一个ProtectionDomain对象的引用和这些class文件的字节传递给defineClass( )方法。这个defineClass( )方法将Friend和Friend$1所在的方法区中的类型数据和被传递的ProtectionDomain对象相关联。

2.9 访问控制器

java.security.AccessController提供了一个默认的安全策略执行机制,它使用栈检查来决定潜在不安全的操作是否被允许。

由AccessController的checkPermission( )实现的基本算法决定了调用栈中的每个帧是否有权执行潜在不安全的操作。每一个栈帧代表了由当前线程调用的某个方法,每一个方法是在某个类中定义的,每一个类又属于某个保护域,每个保护域包含一些权限。因此,每个栈帧间接地和一些权限相关。

当调用doPrivileged( )方法时,就像调用其它任何方法一样,都会将一个新的栈帧压入栈。在由AccessController执行的栈检查中,一个doPrivileged( )方法调用的栈帧标识了检查过程的提前终止点。如果和调用doPrivileged( )的方法相关联的保护域拥有执行被请求操作的权限,AccessController将立即返回。这样这个操作就被允许,即使在栈下层的代码可能没有执行这个操作的权限。

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

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,598评论 18 399
  • (一)Java部分 1、列举出JAVA中6个比较常用的包【天威诚信面试题】 【参考答案】 java.lang;ja...
    独云阅读 7,085评论 0 62
  • 时差党,美高生,坐标加州。 喜欢看书和写作,酷爱风花雪月。 悲观主义者,周期性焦虑。 对股市感兴趣。未来打算去念经...
    眠旧时书阅读 233评论 0 0
  • 写不出东西的我 脑子一片混沌 像被关在一个封闭的大房子 四面的墙上涂满了白漆 我盘坐在最中央 窒息感,孤独感 充斥...
    做个作家梦阅读 290评论 0 1
  • 2017年,对我而言,是一个悲喜交织的年份,悲的是,我的父亲在今年夏天突然离世,我成了没有双亲的人,父母在,人生尚...
    快乐英子阅读 207评论 1 5