In the early days of Java, life was simple, security policy was black and white, and external code was summarily untrusted. Since then, life has become more complex, security policy is now made to order, and code runs the gamut from trusted to untrusted (encompassing every state in between). Figure 1.6 in Chapter 1 illustrates the difference in trust models. It is easiest to get a grasp on the notion of trust assigned to Java classes by harkening back to ancient Java history circa 1996.
As Figure 2.8 shows, in JDK 1.0 there are only three possible paths that byte code can take through the Java security model. The path taken is determined by the origin of the byte code. Built-in byte code from the JDK 1.0 distribution (off the local disk) is allowed to bypass the verification stage. This means that built-in classes are assumed to be both correct and well behaved. All other JDK 1.0 byte code classes must pass through the strict security checks.
It is not just JDK classes that can bypass security. In JDK 1.0, any class stored on the local file system and made part of the local approved set of classes will be considered a built-in class. Such classes are, however, only subject to automatic loading if they are placed in one of the subdirectories in the CLASSPATH. Making a change to the CLASSPATH, or placing a new class in one of these subdirectories is tantamount to making a fundamental change in your security policy. A good example of classes given built-in status is the set of class files included with Netscape Navigator: classes.zip.
Under JDK 1.0, it is essential that you never install classes of unknown origin as built-in classes by putting them in the CLASSPATH. Since the security checks are bypassed for this kind of byte code, granting such classes built-in status is very dangerous. Install only vendor-supplied classes as built-in classes.
The file system loader (aka the Primordial Class Loader) shown in Figure 2.8 sets up the namespace for built-in classes. This namespace is special. The local namespace is exclusively reserved for built-in classes. This helps protect built-in classes (including essential classes like the Security Manager) from being spoofed by outside classes.
Built-in classes may or may not be subjected to the Security Manager, depending on the policy of the site. Vendor-supplied classes often require unhindered access to system resources. Classes like the ClassLoader must be trusted in order to bootstrap the system.
In summary, under JDK 1.0, built-in classes and classes loaded from the CLASSPATH are trusted classes. Classes loaded from a URL (either a file: type or an http: type) are untrusted classes.
Under JDK 1.1 the same system applies as was the case in JDK 1.0. However, a class loaded from the network can become trusted if it is digitally signed by a party whom the user has decided to trust. The idea is to allow code signed by a trusted party to attain the same level of privilege as is usually afforded to built-in code. This is a decision not to be taken lightly. Security ramifications are just as critical as those raised when adding classes to the CLASSPATH. Note that the way JDK 1.1 is used in practice, the resulting trust model is still black and white. Code is still either completely trusted, or it's not trusted at all.
With Java 2, things get even more complicated. Built-in classes are still completely trusted, but external, digitally signed classes can become partially trusted at the whim of the user. By adding partial trust and access control to the VM, Java 2 opens the door for complex, fine-grained security policies that grant privileges according to identity. See Figure 2.9.
Chapter 3 is devoted to understanding the subtleties of this new approach to Java security.
Copyright ©1999 Gary McGraw and Edward Felten.