
|
How the sandbox simultaneously evolved into JDK 1.2 and devolved into Card Java by Gary McGraw and Edward Felten
Portions of this article are taken by permission from "Securing Java: Getting Down to Business with Mobile Code" (John Wiley and Sons, 1998) the second edition of McGraw and Felten's groundbreaking book "Java Security: Hostile Applets, Holes, & Antidotes". |
|
Java security is more important than ever. Since its introduction in 1995, Java has become one of the most popular development platforms on the planet. In fact, Java has been widely adopted more quickly than any other computer language. It now easily tops the list of preferred platforms for Internet-savvy mobile code. There are tens of thousands of Java developers (some say hundreds), and demand for Java skills appears to be growing. Java is definitely here to stay. Java holds great promise as a platform for component-based software, embedded systems, and smart cards. That means Java is poised to play an important enabling role in e-commerce as these systems move from ether-ware into the enterprise. Java components (a.k.a. JavaBeans) are appearing at a rapid pace, and encapsulate critical functionality for transaction-based systems. Java smart cards for e-commerce will debut soon. But what of the hue and cry over security? Should people be so concerned about the security implications of Java that they disable Java in their browsers? Should developers avoid using Java in their systems in favor of other languages like C++? Should system administrators block Java content at the firewall (or better yet, can they)? Should business people avoid Java because of security problems? These are the some of the questions this article begins to answer. The answers are non-trivial, and the issues are as complex as they are important.
There are currently a large and growing number of Java systems running the gamut from Java gizmos (including Java rings), through smart cards with built-in Java interpreters, to complete Java Development Kits and IDEs. As with any platform meant to interact in a networked world, there are security concerns with each flavor of Java. Counterintuitively, Java is both growing and shrinking at the same time. The JDK, now up to version 1.2, is doubling in size with each major release. At the same time, embedded Java systems like Card Java 2.0 are stripping Java functionality down to bare bones. Both of these moves have important security implications. JDK 1.2 involves fundamental changes to the Java security model as the Java sandbox metamorphosing itself into a trust-based system built on code signing. Card Java 2.0 removes much of the sandbox, leaving smart card applets more room to misbehave. This short article addresses new Java security issues found at both ends of the Java spectrum. Much more information can be found in our book "Java Security Risks and Realities" (John Wiley & Sons, 1998).
Mobile code and security The idea of sending around data that can be automatically executed wherever it arrives, anywhere on the Net, regardless of platform, is an intriguing one. Such data that ends up being treated as code is called both "executable content" and "mobile code." Languages like Java, Safe-Tcl, Telescript, Word macros, Excel macros, ActiveX, and Postscript all provide systems for executable content. This powerful idea opens up many new possibilities on the World Wide Web, and not just on the client side, but on the server as well. Full-fledged Web-based programs have already appeared and many more are in the works. But mobile code is no free lunch. The fantastic potential of executable content is hindered by serious security concerns. Security is always an issue when computers are networked together. Realistically speaking, there is no such thing as a computer system that is 100% secure. Because of this fact, users of networked computers must weigh the benefits of being connected to the world against the risks that they incur by connecting in the first place. In the case of executable content, the very idea is to run somebody else's untrusted code locally on your machine. This makes security experts rather nervous. How do you know that the foreign code won't do something nasty? There are two major approaches addressing the security concerns raised by executable content systems: sandboxing and code signing. The first of these approaches, sandboxing, is an idea embraced by early implementations of Java (JDK 1.0.2). The idea is simple --- make untrusted code run inside a box and limit its ability to do risky things. That is exactly what the Java security model aims to do. The second approach, code signing, is how the ActiveX Authenticode system works. Binary objects, like ActiveX controls or Java class files can be digitally signed by someone who "vouches" for the code. If you know and trust that person or organization, you may choose to trust the code they vouch for. It is important to stress the fact that code signing is not a security model. Instead, it is a trust model for authentication and authorization. So which of these systems is better? And what will executable content systems like Java and ActiveX look like in the future? We'll begin to answer these questions by quickly reviewing the Java security sandbox.
Java-enabled Web browsers, including Netscape Navigator and Microsoft Internet Explorer, allow Java applet code to be embedded in a Web page, downloaded across the net, and run on a local machine. Java-based Web servers, including JavaSoft's Java Web Server (JWS) or Jef Poskaner's free Acme.Serve, allow Java servlets to run on the server side. For both of these kinds of system security is of critical concern. Java applets are exceptionally easy to download---sometimes without even knowing it. This exposes Web users to a significant amount of risk. One false click and you could be toast. Java servlets introduce similar risks to CGI programs. Java's designers are well aware of many of the risks associated with executable content. To combat these risks, Java was specifically designed with security concerns in mind. The main goal was to address the security issue head-on so that naive users would not have to become security experts just to surf the Web. This is an admirable goal. In early versions of Java (which encompass the versions still used in the most popular Web browsers), security was based on three pieces of a system called the sandbox: the Byte Code Verifier, the Class Loader, and the Security Manager. Together, these three parts perform load- and run-time checks to restrict file-system and network access, as well as access to browser internals. Each of these prongs depends in some way on the others. For the security model to function properly, each part must do its job properly. Much more information on the Java Security model can be found on the Java Security Web Site (http://www.rstcorp.com/java-security.html). With the introduction of JDK 1.1, Java's sandbox model underwent a state transition from a required model applied equally to all Java applets to a malleable system that could be expanded and personalized on an applet-by-applet basis. The addition of code signing to Java really does complicate things. As it now stands, the Java sandbox has been reduced to a default.
ActiveX is a high-profile form of mobile code promoted by Microsoft. Note that its "mobility" is completely constrained to one platform, however. ActiveX is language independent, but not platform independent, meaning ActiveX controls work only on Microsoft's win32 platform (Windows 95 and Windows NT). One caveat, comparing ActiveX and Java is somewhat like comparing apples and oranges, even though everybody does it. ActiveX is a component-based software model while Java is a language/platform. ActiveX should really be compared with Java components, JavaBeans. In fact, some argue that the real religious Holy war between Java and ActiveX is destined to take place in the middleware arena and will be decided by the battle of component models (see Ted Lewis's "Java Holy War '98" [2]). The first thing to know about ActiveX is that it does not have a security model. It has a trust model that may be able to help you implement your own security policy. So the real question is: how does a trust model like ActiveX's compare with a sandbox like Java's? ActiveX has been roundly criticized by computer security professionals because its approach to security is seen as lacking. Unlike the base Java security situation, in which an applet has to run in the sandbox and is limited in the sorts of things it can do, an ActiveX control has no limitations on its behavior once it is invoked. The upshot is that users of ActiveX must be very careful only to run completely trusted code. On the other hand, Java users have the luxury of running untrusted code fairly safely. The ActiveX approach relies on digital signatures, a kind of encryption technology in which arbitrary binary files can be "signed" by a developer, distributor, or certifier. Because a digital signature has special mathematical properties, it is irrevocable and unforgeable. That means a program like your browser can verify a signature, allowing you to be absolutely certain who vouched for a piece of code (as long as people are carefully guarding and managing the private keys used to sign code). Better yet, you can instruct your browser always to accept code signed by some party that you trust, or always to reject code signed by some party that you don't trust. The signature also supplies data integrity, meaning it can ensure that the code you received is the same as the code that was originally signed. Signed code cannot be easily hijacked and Trojan'ed. The ActiveX system provides a black and white trust model: either you trust the code completely and allow it to run unhampered on your machine, or you don't. That means trusting the wrong sort of code just once is all it takes. Once an attack control (an evil ActiveX component) runs on your system, it can rewrite your security policy in such a way that all future attacks will work. Of course, it can do anything at all, so this is only one of zillions of attack scenarios. Serious attacks using ActiveX have been seen in the wild (though their use is not widespread). For an explanation of these attacks and more on ActiveX insecurity, see Anup Ghosh's book "E-Commerce Security" [3].
Do digital signatures make ActiveX more attractive security-wise than Java? No; especially in light of the fact that digital signature capability became available in Java's JDK 1.1 and in combination with fine-grained access control plays a major role in JDK 1.2 security. That means in Java, you get everything that ActiveX is doing for security plus the ability to run untrusted code fairly safely. When combined with access control, code signing allows applets to step outside the security sandbox gradually. In fact, the entire meaning of sandbox becomes a bit vague. As an example of how Java code-signing might work, an applet designed for use in an Intranet setting could be allowed to read and write to a particular company database as long as it was signed by the system administrator. Such a relaxation of the security model is important for developers who are chomping at the bit for their applets to do more. Writing code that works within the tight restrictions of the sandbox is a pain, and the original sandbox is very restrictive.
How do you sign a Java class file? The key to certification and authentication is the use of digital signatures. The concept is simple: to provide a way for people to "sign" electronic documents so that these signatures can be used in the same way we use signatures on paper documents. Bruce Schneier points out in his excellent book "Applied Cryptography" that in order to be useful, a digital signature should satisfy five properties [4]. It should be: verifiable, unforgeable, non-reusable, unalterable, and non-deniable. Mathematicians and computer scientists have devised several digital signature schemes that appear to work quite well. Unfortunately for developers, many competing commercial systems for code signing are appearing, and they don't interoperate. No matter which code signing system you use, signatures alone don't provide the infrastructure needed to allow Java code out of the sandbox *gradually*. In JDK 1.1, for example, applet code signed by a trusted party can be treated as trusted local code, but not as partially-trusted code. There is no notion of access control beyond the one and only trust decision made per class. That means JDK 1.1 offers a black and white trust model much like ActiveX (though with the clear advantage that untrusted code must stay in the sandbox). JDK 1.2 introduces the idea of protection domains to Java. This idea serves as the basis for making security-critical access control decisions and makes it possible to move beyond black and white trust models like the ones implemented in JDK 1.1 and ActiveX. Protection domains open the possibility of providing fine-grained access control and extensible control structures, but at the same time require careful security policy creation and management. In the end, the idea is simple---make all code run under a security policy that grants different amounts of privilege to different programs---while in practice, creating a coherent policy is quite hard. JDK 1.2 code running on the new Java VMs can be granted permissions and have its access checked against policy when it runs. The cornerstone of the system is policy (something that will not surprise security practitioners in the least). Policy can be set by the user (usually a bad idea) or by the system administrator and is represented in the class java.security.Policy. Herein rests the Achilles' Heel of JDK 1.2 security. Setting up a coherent policy at a fine-grained level takes experience and security expertise. Today's harried system administrators are not likely to enjoy this added responsibility. On the other hand, if policy management is left up to users, mistakes are bound to be made. Users have a tendency to prefer "cool" to "secure". In fact, as one of us (Felten) has been known to say every so often, "Given the choice between dancing pigs and security, users will pick dancing pigs every time." Executable code is categorized based on its URL of origin and the public keys corresponding to the private keys used to sign the code (public/private keys come in pairs). The security policy maps a set of access permissions to code characterized by particular signature/origin information. Protection domains can be created on demand and are tied to code with particular CodeBase and SignedBy properties. If this paragraph confuses you, imagine trying to create and manage a coherent mobile code security policy! An example of how this works in practice is helpful. First, imagine a policy representing the statement: code from http://www.rstcorp.com/applet signed by "self" is given permission to read and write files in /applet/tmp and connect to any host in the rstcorp.com domain. Next, a class that is signed by "self" and that originates from http://www.rstcorp.com/applet arrives. As the code runs, access control decisions are made based on the permissions defined in the policy. The permissions are stored in permission objects tracked by the Java runtime system. Technically, access control decisions are made with reference to the runtime call stack associated with a thread of computation. Code can be signed with multiple keys can potentially match multiple policy entries. In this case, permissions are granted in an additive fashion. More technical detail regarding implementation of the JDK 1.2 security model can be found in the paper "Implementing Protection Domains in the JDK 1.2" by Li Gong and Roland Schemers [5]. Our book also provides more information [1].
JDK 1.2 introduces significant changes to the Java security landscape. Widespread support for code signing systems is appearing on consumer desktops worldwide. This is good news for developers who want less restriction placed on their applets. But with code signing comes a host of new risks to manage; most notably, the risk that the implementation will have holes and the risk that security policies will get too complicated to understand and manage.
At the same time the Java security landscape is undergoing major changes with the introduction of JDK 1.2, Java is also downsizing into a platform for smart cards. From a security perspective, this is a mixed blessing. Smart cards are currently being touted as a partial solution to open problems in e-commerce security, and their combination with Java has interesting implications. So what are smart cards anyway, and what is the impact of running Java on them?
A smart card looks just like a credit-card only with a chip embedded into its plastic. Imagine replacing the hologram on a standard credit card with a similarly-thin chip, and you get the idea. Most smart card chips are about the size of a dime (only thinner) and can be recognized by their distinctive gold terminals. A smart card chip is actually a complete little computer with non-volatile memory, storage, a card operating system (COS), and accompanying communication protocols. The most advanced smart cards on the market have the processing power once found in an IBM-XT (with less memory, of course). There are lots of different kinds of smart cards: security cards (used to identify the carrier), electronic wallet cards (with stored value), processor cards (which carry out proprietary calculations in a black box fashion), memory cards, and even cards with virtual machines to run Java applets on! Unlike traditional computers, smart cards do not have a built-in power supply. Instead, they require a terminal to work. Such a terminal is usually called a smart card reader. The card is inserted into a reader which powers up the card and sets it up to receive software commands. There are many custom command sets for smart cards, but most are based around the ISO 7816 family of specifications. ISO 7816 defines commands in great detail and lays out communication protocols used by smart cards. Smart cards have long been associated with security since they provide a partial solution to the need for personal identification and non-repudiation. To the extent that a card is tamper-resistant, it can be used to store important secrets such as DES keys or RSA private keys.
One obstacle blocking widespread use of smart cards in US markets has been the large number of incompatible and often obscure development languages available for writing smart card applications. Regardless of the ISO 7816 specifications, programming languages for smart cards have traditionally amounted to special-purpose assembly languages. Few developers were familiar with card application languages; the upshot being that only a handful of people could develop smart card code. As cards become computationally more powerful, new application languages are being designed and put into use. One of the most interesting new systems is Java Card 2.0 (see http://www.javasoft.com/products/javacard/index.html). A JavaCard is a smart card that is able to interpret Java byte code, similar to the way Java-enabled browsers can. Card Java is a stripped-down flavor of Java based-on a subset of the Java API (plus some special-purpose card commands). To be capable of running Card Java, a smart card must have at least 16K of read-only memory, 8K of EEPROM, and 256 bytes of random access memory. Given a virtual machine on a smart card, the number of possible new applications is mind-boggling. With an off-the-shelf (or off-the-Net) application development environment for Card Java, thousands of Java developers will be able to program smart cards. Of course, the memory and interface constraints of smart cards will deeply affect programming style. Card Java has many features familiar to Java developers: packages, dynamic object creation, virtual methods, interfaces, and exceptions. Elements of Java that are not supported include: dynamic class loading, a security manager, threads, cloning, garbage collection, and finalization. There are also a number of limitations imposed on runtime cardlet behavior. The "Java Card 2.0 Language Subset and Virtual Machine Specification", a Sun Microsystems document available through the URL given above, describes the smart card version of Java in more detail. In the current Card Java system, applets live on a card forever once they are installed. Access to applets is controlled by the Java Card Runtime Environment (JCRE). The JCRE is made up of the Virtual Machine and the core classes of the Java Card API. It keeps track of applets that are selected and currently active. To the extent that a JavaCard will have important private information stored on it somewhere (like many PCs today), Java security will be as critical as ever.
Smart cards are an excellent medium for carrying password-protected personal data. Private information such as medical records or secret crypto keys can be stored on a card in a form accessible only to the card carrier. In addition, smart cards can store value. Card carriers can decide who to share data with and who to transact business with and use their cards only with those vendors they choose to trust. The most common form of smart card for commerce is the register-based stored-value card. Somewhat ironically, one of the most unfortunate consequence of this kind of smart card is that secret keys on the card are known only to the issuing bank and *must remain secret from the owner*. If the card owner can somehow retrieve a secret key, then they can mint electronic cash.
One of the biggest problems in Java security is figuring out how to preserve type safety while at the same time allowing dynamic class loading. If you can confuse the virtual machine about the types of objects it is manipulating, you can break the security model. We discuss this problem in great detail in our book [1]. Card Java takes care of this problem by removing dynamic class loading (making type safety easier to enforce). In this way, Card Java is less risky than regular Java. Card Java does allow multiple applications to be resident on the same smart card. There is a risk of inter-application attacks. Card Java defines "applet firewalls" meant to protect applets from one another. This must be perfectly implemented to allow safe use of multiple applets. Plans are in the works for smart card applications that cooperate with each other. Imagine, for example, a card that works both as a debit card and as a frequent flyer card. Such plans may introduce more security risk than they are worth. The JCRE is all-knowing. According to Sun, "The JCRE is allowed to use and modify any object on the card." This makes it an especially tempting target for attack. The JCRE must also be perfectly implemented for a Java Card to be secure. New functionality in the form of smart cards promises to help solve some tough real-world problems and address important security concerns. But with new functionality comes new risks. The security dilemma remains: how much risk are you willing to take, and to what benefit?
Java security holes have certainly garnered their share of publicity. But are there still holes now that Java is "growing up"? Vendors have been very good about fixing problems as soon as they are identified, regardless of whether they are implementation bugs or design-level problems (and there have been both). This leads to a more robust system. If Java stood still long enough, you would think all the holes would be discovered and fixed. But Java is far from still. With every major JDK release the Java source code has doubled in size. Much new functionality has been added to the language, some of which has important security implications. The addition of protection domains in JDK 1.2 is a case in point. Implementing a code-signing and access-control system is non-trivial, and the code is certainly security-critical. Then there is the entire security model emasculation problem introduced with Java Card. Because all these systems are complex, subtle security problems are likely to be discovered in these and other new Java subsystems. In the end, the question is: are the benefits of using Java worth the risks? We think they are.
[1] Gary McGraw and Ed Felten (1998) Java Security Risks and Realities: Design and management of secure mobile code, John Wiley & Sons, New York. [2] Ted Lewis (1998) "Java Holy War '98", IEEE Computer, 31(3):126-128. [3] Anup Ghosh (1998) E-Commerce Security: Weak links, best solutions, John Wiley & Sons, New York. [4] Bruce Schneier (1997) Applied Cryptography, second edition, John Wiley & Sons, New York. [5] Li Gong and Roland Schemers, (1998) "Implementing Protection Domains in the Java Development Kit 1.2", In Proceedings of the Internet Society Symposium on Network and Distributed System Security, pp 125-134.
Gary McGraw, Ph.D. is a Senior Research Scientist at Reliable Software Technologies. Dr. McGraw is a noted authority on Java security and co-authored Java Security: Hostile Applets, Holes & Antidotes (John Wiley & Sons, 1997), with Prof. Ed Felten of Princeton University, and a recently published second book, Software Fault Injection: Inoculating Programs Against Errors (John Wiley & Sons) with Dr. Jeff Voas. He has also written over 50 technical publications, consults with major e-commerce vendors including Visa, and is principal investigator on grants from the NSF, Air Force Research Labs, DARPA and NIST's Advanced Technology Program. Ed Felten, Ph.D. is an assistant professor of Computer Science at Princeton University. He leads Princeton's Secure Internet Programming team, whose work in Java Security is widely-publicized and world famous. Prof. Felten received his Ph.D. from the University of Washington and a BS with honors in physics from Cal Tech. Felten was awarded a National Young Investigator award in 1994 and an Alfred P. Sloan Fellowship in 1997. His research interests include computer security (especially relating to the World Wide Web), mobile code security, distributed computing, parallel architectures, and operating systems.
|
Copyright © 1998-9, Gary McGraw and Edward Felten