1.3 敏感数据泄露
系统的安全策略需要确定哪些信息是敏感的。敏感数据可能包括用户信息,比如社会保障或信用卡号码、密码或私钥。在不同等级受信域的组件中共享数据的时候,我们称这些数据是跨越受信边界的。因为在Java环境中,允许在同一个程序中的处在不同受信域的两个组件进行数据通信,从而会出现那些跨受信边界的数据传输。所以,如果在域中存在一个授权用户,而该用户没有数据接收权限,那么系统必须保证这些数据不会发送给处于该域的组件。如果有可能的话,这种处理只简单地禁止数据传输,也有可能对穿越受信边界的数据进行处理,将敏感数据过滤掉,如图1-2所示。
在很多情况下,Java软件组件会输出敏感信息。以下规则可以帮助减轻敏感信息泄露的
在Java中,接口、类和类成员(如字段和方法)是访问受控的。这种访问受控由访问修饰符(public、private或protected)来标识,或者可以通过不使用访问控制符来标识(使用默认的访问控制,也称为package-private访问)。
Java的类型安全是指,在一个字段中被声明为private或者protected的时候,或者是在默认的保护情况下(package), 它是不允许被全局访问的。然而,有一些Java平台的设计会让这种保护失效,从而导致出现安全漏洞,比如对Java反射机制的错误使用。对于Java高手来说,这些漏洞的出现并不奇怪,因为它们在文档中都已做了详细的描述,但一不小心,还是会掉入陷阱。比如说,如果一个字段被声明为public,那么它可以被Java程序中的任意一段代码直接访问,也可以被Java程序中的任意一段代码修改(除非这个数据成员同样被声明为final)。很显然,敏感数据是不允许保存在public域中的,因为如果有人可以直接访问该程序运行所在的JVM的时候,就会招致问题。
表1-1展现了一个简化的关于访问控制规则的视图。x表示在这个地方允许特定的访问。例如,在“class”中,如果标识了x,表示在同一类中声明的代码可以访问该成员。同样,“package”一栏中,如果标识了x,那么表示,这个成员可以被任何定义在同一个包中的类(或者子类)访问,并且该成员可以在由同一个类装载器载入的类(或者子类)中定义。而同一个类加载器的条件仅适用于package-private成员访问的情况。
类和类成员只能给予可能的最低的访问权限,这样的话,恶意代码威胁系统安全的可能性就会大大减少。只要有可能,应当避免使用接口来开放那些包含(或调用)敏感代码的方法;接口应当只允许可以公开访问的方法,而这些方法往往是这个类公开的应用编程接口(API)的一部分。(注意,这和Bloch推荐的优先考虑API接口设计的看法相反 [Bloch 2008,Item 16])。存在一种例外情况,就是在实现一个不可修改的接口的时候,可以开放一个可变对象的公共的不可变部分(见规则OBJ04-J)。此外,值得注意的是,即使一个非final类的可见性是package-private,它仍然容易被错误使用,特别是当它包含公共方法的时候。对于那些执行了所有必要的安全检查,并对所有输入进行了净化的方法,它们同样是可以通过接口开放出来的。
对顶级类来说,protected的访问控制是无效的,虽然对嵌套类来说,可以声明它为protected。对那些非final的公共类的字段来说,它们是不能被声明为protected的,这是为了防止在另一个包中该类的子类的非受信代码可以访问该成员。此外,如果protected成员是这个类的API的一部分,则需要继续得到支持。规则OBJ01-J要求声明私有字段。
当一个类、接口、方法或者一个字段作为API的一部分进行发布的时候,比如说对网络服务接入点来说,它可以被声明为pubic。其他类和成员应当要么声明为package-private,要么声明为private。例如,如果一个类在安全性上的考虑并不是至关重要,它应该定义一个public静态工厂来实现实例控制,这可以通过使用private的构造器来实现。