Chapter 3. Java Class Loaders
Security and the Class Loader
In this chapter, we're going to explore Java's class loading mechanism--the mechanism by which files containing Java bytecodes are read into the Java virtual machine and converted into class definitions. The operation of Java programs depends on the class loader; given Java's desire to ensure security throughout its architecture, it should come as no surprise that class loaders are also a very important piece of the Java security story. The class loader normally works in conjunction with the security manager and access controller to provide the bulk of the protections associated with the Java sandbox.
The class loader is important in Java's security model because initially, only the class loader knows certain information about classes that have been loaded into the virtual machine. Only the class loader knows where a particular class originated, and only the class loader knows whether or not a particular class was signed (although the class loader arranges for the Class object itself to carry its signature with it). Hence, one of the keys to writing a secure Java application is to understand the role of the class loader and to write (or at least use) a secure class loader.
We'll address both those points in this chapter. We begin with an overview of how the class loader functions, and the features that its basic functions add to the overall security of the Java platform. We'll then look into writing our own class loader, the motivation for which will vary depending on the release of Java you're using and the type of application you are running.
As with the other elements of the Java sandbox, the ability to create and use a class loader is limited to Java applications. Java applets use the class loader provided for them by the browser in which they are running, and they are generally prohibited from creating their own class loader.
3.1. Security and the Class Loader
There are two instances where the class loader plays an important role in the Java security model: it must coordinate with Java's security manager or access controller, and it must enforce certain rules about the namespace used by Java classes.
3.1.1. Class Loaders and Security Enforcement
The class loader must coordinate with the security manager and access controller of the virtual machine in order to determine the security policy for a Java program. We'll explore this in more detail in the next few chapters when we discuss these various security mechanisms; for now, we'll just consider the motivation for the following connection.
As we know, a Java applet cannot (normally) read a file when the applet is being run in a browser such as HotJava. The HotJava browser itself, however, can read files, even while it is also running applets. Both the browser and the applets are using the same classes to (attempt to) read a file, so clearly there must be something that allows the java.io classes to determine that one case should fail while the other case should succeed. That differentiation is the by-product of the class loader: the class loader allows the security manager to find out particular information about the class, which allows the security manager to apply the correct security policy depending on the context of the request. When we discuss the security manager, we'll discuss the specific mechanics by which this can be achieved. For now, it is only important to keep in mind that the class loader is the piece of the Java architecture that is able to make this distinction. Since it loaded the class, it knows if the class came from the network (i.e., the class is part of the applet and should not be trusted) or if the class came from the local filesystem (i.e., the class is part of the browser and should be trusted). It also knows if the class was delivered with a digital signature, and the exact location from which the class was loaded. All these pieces of information may be used by the security manager and access controller to establish a security policy.
3.1.2. Class Loaders and Namespaces
The second place where the class loader provides security in Java is more subtle and has to do with Java's namespace rules. Recall that the full name of a Java class is qualified by the name of the package to which the class belongs; there is no standard class called String in the Java API, but there is the class java.lang.String. On the other hand, a class does not need to belong to a package, in which case its full name is just the name of the class. It's often said that these classes are in the default package, but that's slightly misleading: as it turns out, there is a different default package for each class loader in use by the virtual machine.
Consider what happens if you surf to a page at www.sun.com and load an applet that uses a class called Car (with no package name); after that, you surf to a page at www.ora.com and load a different applet that uses a class called Car (also with no package name). Clearly, these are two different classes, but they have the same fully qualified name--how can the virtual machine distinguish between these two classes?
The answer to that question lies in the internal workings of the class loader. When a class is loaded by a class loader, it is stored in a reference internal to that class loader. A class loader in Java is simply an object whose type is some class that extends the ClassLoader class. When the virtual machine needs access to a particular class, it asks the appropriate class loader. For example, when the virtual machine is executing the code from sun.com and needs access to the Car class, it asks the class loader that loaded the applet (r1 in Figure 3-1) to provide that class.
Figure 3-1. Different instances of the class loaders help to disambiguate class names
In order for this scheme to work, the Car class from www.ora.com must be loaded using a different class loader than that which loaded the Car class from www.sun.com. That way, when the virtual machine asks the class loader r2 for the definition of the Car class, it will get back (correctly) the definition from ora.com. The class loader does not need to be a different class; as this example implies, it must merely be a different instance of the class. Hence, applets that have a different CODEBASE (even if they originate on the same host) are always loaded by different instances of the browser's class loader. Applets on the same page with the same CODEBASE, however, may use the same class loader so that they may share class files (as well as sharing other information). Some browsers also allow applets on different pages to be loaded by the same class loader as long as those applets have the same CODEBASE, which is generally a more efficient and useful implementation.
This differentiation between class files loaded from different class loaders occurs no matter what packages are involved. Don't be confused by the fact that there were no explicit package names given in our example. A large computer company might define a class named com.sun.Car, a large oil company might also define a class called com.sun.Car, and the two classes need to be considered as distinct classes--which they will be if (and only if) they are loaded by different instances of the class loader.
So far we've given a logical reason why the class loader is involved in the namespace resolution of Java classes. You might think that if everyone were to follow the convention that the beginning of their package name must be their Internet domain in reverse order--e.g., com.sun for Sun Microsystems--this idea of different class loaders wouldn't be necessary. But there are security reasons for this namespace separation as well.
In Java, classes that are members of the same package have certain privileges that other classes do not have--they can access all the classes of the package that have the default protection (that is, the classes that are neither public, private, nor protected). Additionally, they can access any instance variable of classes in the package if the instance variable also has the default protection. As we discussed in Chapter 2, "Java Language Security", the ability to reference only those items to which a class has access is a key part of the security restrictions Java places on a program to ensure memory and API integrity.
So let's assume that no class loader based package separation exists, and that we rely on Sun Microsystems to name its classes com.sun.Car and so on. Everything would proceed reasonably, until we surf to www.EvilSite.org, where someone has placed a class called com.sun.DoSomethingEvil. Without the namespace separation introduced by the class loader, this class would suddenly have access to all the default protected classes and default protected variables of every class that had been downloaded from Sun. Worse, that site could supply a class called com.sun.Car with a much different implementation than Sun's--such that when the user (metaphorically, of course) applied the car's brakes, the new implementation sped up instead. Clearly, this is not a desirable situation.
Note too that with a badly written class loader, the hackers at EvilSite.org have the potential to supply new classes to override the core classes of the Java API. When the class loader that loaded the applet from EvilSite is asked to provide the java.lang.String class, it must provide the expected version of that class and not some version from EvilSite.org. In practice, this is not a problem, because the class loader is written to find and return the core class first.
Without enforcement of the namespace separation that we've just outlined, there is no way to ensure that the hackers at EvilSite.org have not forged a class into the com.sun package. The only way to prevent such forgeries would be to require that every class be a signed class which authenticated that it did in fact come from sun.com (or wherever its package name indicates that it should have come from). Authenticated classes certainly have their place in Java's security model, but it would be unmanageable to require that every site sign and authenticate every class on its site.
Hence, the separation of classes based on the class loader that loaded them--and the convention that applets on different pages are loaded by different class loaders--has its benefits for Java security as well as solving a messy logistical problem. We'll now look into the details of how the class loader actually works.
Copyright © 2001 O'Reilly & Associates. All rights reserved.