Sponsored Link •
Aside from platform independence, discussed in the previous chapter, the other major technical challenge a network-oriented software technology must deal with is security. Networks, because they allow computers to share data and distribute processing, can potentially serve as a way to break into a computer system, enabling someone to steal information, alter or destroy information, or steal computing resources. As a consequence, connecting a computer to a network raises many security issues.
To address the security concerns raised by networks, Java's architecture comes with an extensive built- in security model, which has evolved with each major release of the Java Platform. This chapter gives an overview of the security model built into Java's core architecture and traces its evolution.
Java's security model is one of the key architectural features that makes it an appropriate technology for networked environments. Security is important because networks represent a potential avenue of attack to any computer hooked to them. This concern becomes especially strong in an environment in which software is downloaded across the network and executed locally, as is done, for example, with Java applets and Jini service objects. Because the class files for an applet are automatically downloaded when a user goes to the containing web page in a browser, it is likely that a user will encounter applets from untrusted sources. Similarly, the class files for a Jini service object are downloaded from a code base specified by the service provider when it registers its service with the Jini lookup service. Because Jini enables spontaneous networking in which users entering a new environment look up and access locally available services, users of Jini services will likely encounter service objects from untrusted sources. Without any security, these automatic code download schemes would be a convenient way to distribute malicious code. Thus, Java's security mechanisms help make Java suitable for networks because they establish a needed trust in the safety of executing network-mobile code.
Java's security model is focused on protecting end-users from hostile programs (and bugs in otherwise benevolent programs) downloaded across a network from untrusted sources. To accomplish this goal, Java provides a customizable "sandbox" in which untrusted Java programs can be placed. The sandbox restricts the activities of the untrusted program. The program can do anything within the boundaries of its sandbox, but can't take any action outside those boundaries. For example, the original sandbox for untrusted Java applets in version 1.0 prohibited many activities, including:
Because the sandbox security model imposes strict controls on what untrusted code can and cannot do, users are able to run untrusted code with relative safety. Unfortunately for the programmers and users of 1.0 systems, however, the original sandbox was so restrictive, that well-meaning (but untrusted) code was often unable to do useful work. In version 1.1, the original sandbox model was augmented with a trust model based on code signing and authentication. The signing and authentication capability enables the receiving system to verify that a set of class files (in a JAR file) has been digitally signed (in effect, blessed as trustworthy) by some entity, and that the class files have not been altered since they were signed. This enables end users and system administrators to ease the restrictions of the sandbox for code that has been digitally signed by trusted parties.
Although the security APIs released with version 1.1 include support for authentication, they don't offer much help in establishing anything more than an all-or-nothing trust policy (in other words, either code is completely trusted or completely untrusted). Java's next major release, version 1.2, provided APIs to assist in establishing fine-grained security policies based on authentication of digitally signed code. The remainder of this chapter will trace the evolution of Java's security model from the basic sandbox of 1.0, through the code signing and authentication of 1.1, to the fine-grained access control of 1.2.
In the world of personal computers, you have traditionally had to trust software before you ran it. You achieved security by being careful only to use software from trusted sources, and by regularly scanning for viruses just to make sure. Once some software got access to your system, it had full reign. If it was malicious, it could do a great deal of damage because there were no restrictions placed on it by the runtime environment of your computer. So in the traditional security scheme, you tried to prevent malicious code from ever gaining access to your computer in the first place.
The sandbox security model makes it easier to work with software that comes from sources you don't fully trust. Instead of approaching security by requiring you to prevent any code you don't trust from ever making its way onto your computer, the sandbox model allows you to welcome code from any source. But as code from an untrusted source runs, the sandbox restricts the code from taking any actions that could possibly harm your system. You don't need to figure out what code you can and can't trust. You don't need to scan for viruses. The sandbox itself prevents any viruses or other malicious or buggy code you may invite into your computer from doing any damage.
If you have a properly skeptical mind, you'll need to be convinced a sandbox has no leaks before you trust it to protect you. To make sure the sandbox has no leaks, Java's security model involves every aspect of its architecture. If there were areas in Java's architecture where security was not considered, a malicious programmer (a "cracker") could likely exploit those areas to "go around" the sandbox. To understand the sandbox, therefore, you must look at several different parts of Java's architecture, and understand how they work together.
The fundamental components responsible for Java's sandbox are:
One of the greatest strengths of Java's sandbox security model is that two of these components, the class loader and security manager, are customizable. By customizing these components, you can create a customized security policy for a Java application. Unfortunately, this customizability doesn't come for free, because the very flexibility of the architecture creates some risks of its own. Class loaders and security managers are complicated enough that the mere act of customization can potentially produce errors that open up security holes.
In each major release of the Java API, changes were made to make the task of creating a custom security policy less prone to error. The most significant change occurred in version 1.2, which introduced a new and more elaborate architecture for access control. In version 1.0 and 1.1, access control, which involves both the specification of a security policy and the enforcement of that policy at run time, is the responsibility of an object called the security manager. To establish a custom policy in 1.0 and 1.1, you have to write your own custom security manager. In 1.2, you can take advantage of a security manager supplied with the Java 2 Platform. This ready made security manager allows you to specify a security policy in an ASCII policy file separate from the program. At runtime, the ready made security manager enlists the help of a class called the access controller to enforce the security policy specified in the policy file. The access control infrastructure introduced in 1.2 provides a flexible and easily customized default implementation of the security manager that should suffice for the majority of security needs. For backwards compatibility, and to enable parties with special security needs to override the default functionality provided by the ready made security manager, version 1.2 applications can still install their own security manager. Using the ready made made security manager, and the extensive access control infrastructure that comes with it, is optional.