Sie sind auf Seite 1von 253

Preface

Chapter One Mobile Code and Security: Why Java Security is Important
Chapter Two The Base Java Security Model: The Original Applet Sandbox
Chapter Three Beyond the Sandbox: Signed Code in JDK 1.2
Chapter Four Malicious Applets: Avoiding the Common Nuisances
Chapter Five Attack Applets: Exploiting Holes in the Security Model
Chapter Six Securing Java: Improvements, Solutions, and Snake Oil
Chapter Seven Java Security Guidelines: Developing and Using Java More Securely
Chapter Eight Java Card Security: How Smart Cards and Java Mix
Chapter Nine The Future of Java Security: Challenges Facing Mobile Code
Appendix A Frequently Asked Questions:Java Security FAQ
Java versus Active X
Appendix B Java Security Hotlist
Appendix C How to Sign Java Code
References

Preface
Java has grown by leaps and bounds since its introduction in 1996, and is now among the most
popular computing platforms on the planet. Java has evolved and changed so much that at a mere
two-years old, our original work, Java Security: Hostile Applets, Holes, and Antidotes, found itself
in serious need of revision and expansion. This book is the result of several years of thinking about
mobile code and security, and includes many things we have discovered while working on real-
world systems with businesses and government agencies. Our goal is to present enough information
to help you separate fact from fiction when it comes to mobile code security.

Java has become much more complicated and multifaceted than it was when it was introduced. No
longer simply a client-side language for applets, Java can now be found on everything from
enterprise application servers to embedded devices like smart cards. We have tried to address
security factors from throughout the entire Java range in this book.

We hope this book appeals to geeks and grandmothers alike (not that some grandmothers aren't
geeks). Although it gets technical in places, we hope the messages are clear enough that even the
casual Web user comes away with a broader understanding of the security issues surrounding
mobile code. We kept four groups in mind as we wrote this book: Web users, developers, system
administrators, and business decision-makers. Many of the issues of mobile code security cut
across these groups. As Java integrates itself into the foundations of electronic commerce, Java
security issues take on more urgency.

Java is only one kind of mobile code among many. Other systems immersed in the same security
dilemma include ActiveX, JavaScript, and Word Macros. It is essential not to get the wrong
message from this book. Our focus on Java is no accident. We believe Java is the most viable
mobile code system created to date. Don't believe that through our work we imply that other
systems are any more secure than Java. Just the opposite is true.

With the introduction of code signing to Java (in JDK 1.1) and its enhancement with access control
(in Java 2), securing Java became much harder. Java's position along the security/functionality
tradeoff has moved significantly toward functionality, to the detriment of security. This is good if
you want more functionality, which most businesses and developers seem to need, but it is bad if
you are charged with managing security risks. Forming an intelligent Java use policy is more
important than ever, but doing so is more complicated than it used to be.

The computer field moves so fast that people have begun to refer to Internet time to grapple with its
constantly accelerating speed. Three months is a year in Internet time. Java is directly involved in
the speed of the field, and has done its share to make things move even more quickly. One tricky
aspect of writing a topical book relating to the Web is figuring out when to stop the action. This
process can be likened to freeze-framing a picture of a movie. In that sense, this book is a snapshot
of Java security. We hope we have succeeded in making it a useful way to learn about Java
security. For up-to-date information, see the book's companion Web site at www.rstcorp.com/java-
security.html.

As we went to press, Sun Microsystems renamed JDK 1.2 and called it Java 2. We have attempted
to use correct version numbers throughout and apologize for any confusion.
Chapter 1, "Mobile Code and Security: Why Java Security Is Important,"," sets the stage with a
discussion of the four intended audiences. As Java matures, it is making important inroads into the
enterprise world. That means Java security is now as important to business people and system
administrators as it is to Web users and Java developers. For the uninitiated, Chapter 1 provides a
quick and cursory introduction to Java. Pointers are provided to more through Java texts that cover
the ins and outs of the entire Java language in more detail. This is, after all, not a book on Java per
se, but is instead a book on Java security. We also spend some time discussing why the once-
important distinction between applets and applications has been superceded by concerns about
trust. It turns out that under the Java 2 architecture, applets can be completely trusted and
applications can be completely untrusted. In fact, every kind of Java code can be doled out different
amounts of trust, depending on what the user's policy says. Finally, we cover some other popular
forms of mobile code and discuss how their security stacks up against Java. The main purpose of
this chapter is to provide some context for the later discussion of Java's critical security
implications and to introduce the central idea of the book: weighing the benefits of Java use against
the risks.

Chapter 2, "The Base Java Security Model: The Original Applet Sandbox," examines the base Java
security model in some detail. As a prelude to our discussion, we introduce four categories of
attacks, ranging from the very serious to the merely annoying: system modification, invasion of
privacy, denial of service, and antagonism. We then discuss Java's programming-languages
approach to security and introduce the three parts of the original applet sandbox. These include the
Verifier, the Class Loader Architecture, and the Security Manager. We also introduce the idea that
Java security fundamentally relies on ensuring type safety. The base sandbox provides the
foundation of Java's new trust-based security model. Starting with a restrictive sandbox for
untrusted code, restrictions can be lifted little by little until code takes on complete trust and is
awarded full run of the entire system.

Chapter 3, "Beyond the Sandbox: Signed Code and Java 2," examines Java's new trust-based
security model. With the addition of code signing in JDK 1.1, Java's security architecture
underwent a large shift. Java 2 completed the transformation with the addition of access control. It
is now possible to create complex security policy for mobile code written in Java and have the Java
system itself enforce the policy. The change certainly affords more power to mobile code than ever
before, but it also introduces a major new risk to Java: a human-centered policy management risk.
Setting up and managing a mobile code policy will be a complex and error-prone undertaking
requiring security experience. JDK 1.1 and Java 2 rest on the notion of trust, which leverages the
technological power of code signing. Understanding the new model requires understanding the way
code signing and trust interact, and discounting some of the common myths associated with it.
Chapter 3 ends with a discussion of stack inspection and the Java 2 code-signing API. (Appendix
C, "How to Sign Java Code," is a code-signing tutorial covering Microsoft, Netscape, and Sun's
three different code signing schemes.)

Chapter 4, "Malicious Applets: Avoiding a Common Nuisance," begins to discuss what happens
when the Java security model is abused by hostile applets. Hostile applets come in two forms: very
dangerous attack applets that involve security breaches, and merely annoying malicious applets that
are more of a nuisance than anything else. Chapter 4 is all about malicious applets. Malicious
applets are quite easy to create, and they are equally easy to find on the Web. Unfortunately, there
are just as many unscrupulous individuals on the Net as there are in the rest of the world. Bad guys
are more than happy to include Java in their list of offensive weapons. Our mission is to make Java
users aware of common classes of attacks.

Chapter 5, "Attack Applets: Exploiting Holes in the Security Model," delves more deeply into the
Java security model by focusing attention on some of the well-publicized security holes that have
been discovered. This is where our discussion of hostile applets turns more serious. Securing Java
is a difficult job, especially when it comes to implementing complicated models. Attack applets
have been created in the lab that exploit the holes we discuss. Some of the holes are simple
implementation bugs, while others indicate more serious design flaws. The good news is that Sun
and other licensees take Java security very seriously and they respond quickly to fix any holes once
they are discovered. We think discussing these holes is important since it emphasizes the true
nature of computer security.

Chapter 6, "Securing Java: Improvements, Solutions, and Snake Oil," has two overall goals, both of
which are meant to impact the Java security situation positively. The first is to suggest some high-
level antidotes for Java security concerns that are not tied to particular attacks. Experts in computer
security have pointed out several global deficiencies in the Java approach to security. Fixing some
of these would certainly improve the model. High-level concerns addressed in Chapter 6 include
programming language issues, formal analysis of Java, applet logging, trust, decompilation, applet
monitoring, and policy management. Hopefully, some of the high-level concerns we raise will
eventually be addressed in the Java platform itself. In the meantime, a number of third-party
vendors are eager to help. The second goal of Chapter 6 is to introduce the players briefly and to
discuss what risks third-party vendors can and cannot address. The computer security field has its
share of snake oil, and complex issues such as mobile code security tend to be easy to exploit. One
of our goals is to bring some realism to the table and arm you with the right questions to ask.

If you only read one chapter of this book, read Chapter 7, "Java Security Guidelines: Developing
and Using Java More Securely." This chapter presents two sets of guidelines: one for Java
developers and one for Java users. Writing security-critical code is not easy, and developers need
all the help they can get. We offer 12 rules for writing safer Java. Although the rules get a bit
technical, it is worth spending some time to figure them out. By contrast, our guidelines for Java
users are simple to understand and follow; in fact, most of them are simply common sense.

Chapter 8, "Java Card Security: How Smart Cards and Java Mix," is devoted to Java on smart
cards. We decided to include this chapter since Java Cards are likely to show up in millions of
consumer wallets in the next few years. Smart card security is really too big an issue to cover in a
single chapter, so we focus primarily on the security impact of putting a Java Virtual Machine on a
card. Chapter 8 covers six key questions, including: What is a smart card?, Why put Java on a
smart card?, and How does the use of Java impact smart card security?

We conclude by covering some of the challenges to mobile code that remain to be


conquered. Chapter 9, "The Future of Java Security: Challenges Facing Mobile Code," presents a
concise set of six central lessons we have learned during our time in the Java security trenches. We
go on to discuss several open research issues that you're likely to hear about again. Finally, we
discuss the notion of security assurance, an important strategy in securing Java.
We hope that this book is both informative and useful. Making intelligent decisions regarding the
use of Java (especially in business and other mission-critical systems) requires some knowledge of
the current risks. Our goal is to disclose those risks-and countermeasures to curtail them-as clearly
and objectively as possible. Armed with the knowledge that we present in this book, Java users, site
managers, and business decision-makers can make better Java use policies.

Acknowledgments

This book is a collaborative effort in more ways than one. Not only did the authors work together
closely, but we also sought input from many other people. We are grateful for the help we received.

Reliable Software Technologies (www.rstcorp.com) remains a great place to work. The


intellectually stimulating environment makes going to work interesting and fun. Many people at
RST read drafts of the book or helped in other ways. They include John Viega (intrepid proofreader
and co-author of the code-signing tutorial in Appendix C), Tom O'Connor (who also read the entire
manuscript more than once and co-wrote the code-signing tutorial), Anup Ghosh (fellow security
researcher), Peggy Wallace (travel, anyone?), Lora Kassab (one-time RST intern whose code from
the first edition lives on), Jeff Payne (RST's forward-thinking CEO), Jon Beskin, Matt Schmidt,
Brad Arkin, Andi Bruno (who herds the marketing cats and makes us be nice), and Jeff Voas (who
continues to lead RST's excellent research group by example).

The members of Princeton University's Secure Internet Programming Team


(www.cs.princeton.edu/sip) also provided valuable input. Besides wading through several drafts,
the Princeton team was responsible for raising many of the key issues in Java security. Special
thanks to Drew Dean and Dan Wallach (cofounders of the Princeton team) and Dirk Balfanz. Dan
is now a professor at Rice University. Drew is a research scientist at Xerox PARC. Princeton's
Computer Science department provides a wonderful environment for discovering and exploring
new research topics.

We would also like to thank Tom Cargill, independent consultant and discoverer of two security
flaws; David Hopwood, discoverer of several attack applets; Mark LaDue, creator of the Hostile
Applets Home Page (keep 'em honest, Mark); Dennis Volpano of the Naval Postgraduate School;
Tom Longstaff, research director at the CERT Coordination Center; Roland Schemers, JavaSoft
security implementation wizard (who helped with code-signing tool questions); Marianne Mueller,
Java developer, security expert, and long-suffering target of press inquiries at JavaSoft; Jim
Roskind, Netscape's Java security expert; Andrew Herbert, APM's Chief Scientist in the real
Cambridge; Ken Ayer, chip card security manager at Visa; Don Byrd, UMass research associate
and careful proofreader of the first edition; Hideyuki Hayashi, who translated the first edition into
Japanese (and did an excellent job according to friends at Sumitomo in New York); Kieran
Murphy, editor at developer.com; Chuck Howell, now at Mitretek; and Mike Shoffner, Java
developer at Prominence Dot Com. Li Gong, security architect at JavaSoft, has been a particularly
valuable help, both as a research colleague and as a sane point-of-view at JavaSoft. More power to
you, Li.
Wiley's staff did an excellent job shepherding this book through the editing and production process.
Special thanks to Marjorie Spencer and Frank Grazioli, who went out of their way to make this
project go smoothly. Thanks to Margaret Hendrey for playing fast and loose with extensions (don't
tell anybody). Also thanks to the rest of the team at Wiley. Finally, and most importantly, we're
grateful to our families for putting up with us while we worked on the book, again. Amy Barley,
Jack, and Eli seem to have adjusted to Gary's persistent book-writing. Laura Felten and Claire
suspect that Ed's book-writing has become an addiction. Without the support of our families, this
book would not have been possible.

Mobile Code and Security:


Why Java Security Is Important

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 of
thousands), 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. This means Java is poised to play an important enabling role in e-commerce as these
systems move from ether-ware to reality. Java components (aka 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 book answers. The answers are
nontrivial, and the issues are as complex as they are important.
Chapter One Sections
1. Who Cares?
2. Mobile Code
3. The Power of Networking
4. Downloading Code: Not a New Problem
5. Java in a Demitasse
6. Securing Java
7. How Does Java Security Stack Up?
8. Where to Find More Information on Java
9. Mobile Code Has Its Price
10. Assessing the Risks

Mobile Code and Security: Why Java Security Is


Important
CHAPTER SECTION: 1

Section 1 -- Who Cares?


Next Page

Java security is important to a number of distinct sets of people:

• Web users, including one of the authors' 89-year-old grandmother, need to understand the
risks of using a Java-enabled browser.
• Developers of Java code that lives and works on the Internet need to keep security in mind
when they are writing programs.
• System administrators need to think carefully about how mobile code, including Java,
impacts the security of the systems they run.
• Business people need to understand what Java security risks are so they can make informed
business decisions based on fact and not fiction.

As you can see, Java security issues are multifaceted. This book has useful information for all four
groups, whose interests overlap in many ways.

Java security is a hot topic, but that does not make it an easy one. By itself, computer security is not
well-understood. Throw Java into the mix and things become even murkier. There is much
confusion and misinformation floating around about Java and security. Beware of snake oil,
impossible claims, and consultants who pretend to have all the answers. Also be aware that major
vendors are just as capable of misinformation as fly-by-night companies. Skepticism, Rene
Descartes' 300-year-old philosophical insight, is strangely relevant to computer security at the turn
of the millennium. In fact, skepticism turns out to be an excellent strategy. Ask hard questions; you
might be surprised by the answers.

Browser Beware

The most pressing security concerns surrounding Java impact millions of people-that is, anyone
who browses the Web. Given that there are tens of millions of Netscape Navigator and Microsoft
Internet Explorer users, the client security issue is no minor detail.1 It turns out that a majority of
the users of these browsers are also Java users, whether they know it or not. Java is built in to
Netscape Navigator and Internet Explorer, so if you use either of these products, you are a Java
user. 1

Just as all Internet users are taking security risks, all Java users are taking security risks. Because of
the way Java works, computer security issues are a fundamental concern. Most Java code is
automatically downloaded across the network and runs on your machine. This makes it very
important to limit the sorts of things that Web-based Java programs can do. Simply put, a hostile
Java program could trash your machine. Because Java is inherently Web-based, it provides crackers
with an easy way to implement a Trojan Horse-a program that may seem innocent enough on the
surface, but is actually filled with well-armed Greeks. Also of concern is the problem of computer
virus propagation. Fortunately, the creators of Java have made a good effort to protect users from
these hazards. For example, writing a Web-based Java virus as an applet would be very hard.
(Writing a Microsoft Word macro virus like the concept virus is, by contrast, easy.) Because mobile
code security is new, difficult, and complicated, Java's masters have not always been successful at
protecting everyone all the time.

One goal of this book is to educate Java users about the risks that they incur by surfing the World
Wide Web with Java-enabled browsers. This chapter provides a gentle introduction to Java and
explains why Java is potentially dangerous.

Developer Concerns

Java security is essential to developers as well. As a platform, Java has much to offer in terms of
security:

• Java has advanced cryptography Application Program Interfaces (APIs) and class libraries.
• Java includes language-level security mechanisms that can help make developing secure
code easier.
• Some aspects of Java that make it more difficult to write insecure (unsafe) code.

This book explains how to use the security features built in to the Java environment inside your
own programs. That's not to say that developing secure programs with Java is trivial or automatic.
Anyone who reads the newspapers or the trade press can see how often skilled programmers write
code with security bugs. You can make almost as many gaffes developing security-critical code in
Java as in any other language. Because of Java's security APIs and its position as a leading e-
commerce platform, it is likely that Java will be used to carry out some very important activities.
That means developers need to learn as much as they can about Java security. Know your enemy.
Think about what might confront your code in terms of malicious attacks. Mitigate risks by
designing, coding, and testing carefully.

A second goal of this book is to teach Java developers and project managers about the sorts of
things that will confront their code in "the wild." If you're a seasoned Java developer (something
that it was impossible to be a mere handful of years ago), this book will show you in great detail
how the security model works. There are lessons to be learned from the Java attacks we cover.
After all, like you, Java's designers and developers were serious about what they were doing. As we
have seen, however, even the most subtle bug can be turned into a security disaster.

System Administration and Java

Today's system administrator is seriously overworked, and security is a big part of the problem.
The days of the isolated Local Area Network (LAN) are behind us. Now, most networks are
connected directly to the Internet, which means security is more important than ever. Some early
adopters and sites with a lot to lose try to protect themselves with advanced security mechanisms
such as firewalls, secure shells, and virtual private networks. Many sites, however, have a long way
to go before they are "secure enough" (whatever that means). Mobile code systems, including Java,
make administering site security trickier.

The problem is that users want Java content, but system administrators don't want to take on
unnecessary risks. This is a classic example of the well-known tradeoff in computer security
between functionality and security. Computer security boils down to managing risks, which in turn
implies that the way to make better-informed decisions is to get a handle on the risks.

A third goal of this book is to present an informed discussion of the real risks of mobile code.
Burying your head in the sand like an ostrich is not a good solution, because security problems are
unlikely to miraculously disappear. However, the risks do not necessarily warrant throwing the
Java baby out with the bath water. Such a move may leave your users high and dry.

Even if the risks turn out to be too much to bear (a decision that is very much context dependent),
system administrators need to be wary of snake-oil "solutions" to the mobile code problem. There
are a number of products on the market that purport to improve Java security. The question is, do
they work? We will delve into these issues as well.

Java Gets Down to Business

Making informed business decisions at the edge of the technology curve has never been an easy
task. In addition to the technological concerns discussed earlier, there are often intangible factors to
consider. What impact will perceived security risks (whether justifiable or imagined) have on
potential customers? Is Java the best platform to use when designing e-commerce systems? How
will the use of Java within an enterprise affect security risks? What are the security challenges in
designing and deploying database-backed Web servers and three-tier applications?

It is surprising that some of the same companies that disallow the use of Java (often for silly
reasons) expect their customers and business partners not to disallow Java. The information in this
book can help business managers and leaders make better decisions about Java security. Good data
are essential to decision-making, but sometimes good data are hard to find.

CHAPTER SECTION: 2

Section 2 -- Mobile Code


The Java programming environment from Sun Microsystems is designed for developing programs
that run on many different kinds of networked computers. Because of its multiplatform capabilities,
Java shows great promise for relieving many of the headaches that developers encounter when they
are forced to migrate code between different types of operating systems. Code that is written in
Java should run on all of the most popular platforms-everything ranging from Macintosh and
Windows/Intel machines to Linux and Solaris boxes.

Recently, the cross-platform capabilities of Java have been called into question. This has led Sun's
marketing phrase "write once, run anywhere" to be reinterpreted by skeptics as "write once, test
everywhere." Part of the problem is that not all implementations of Java are completely
interoperable with Sun's version. Disagreement over what constitutes Java has generated at least
one high-profile lawsuit. Most people, including a majority of Java developers, would like to see
Java become a standard so that what happened to C (which was itself supposed to be a cross-
platform language) doesn't happen to Java.

In any case, a nice side effect of Java's built-in portability is that one special kind of Java program
(popularly known as an applet) can be attached to a Web page. More technically speaking, applets
are embedded into a Web page's hypertext markup language (HTML) definition and executed by
Java-savvy browsers.2 Such Java-enabled browsers automatically download and begin running any
Java applet they find embedded in a Web page. Java code's ability to run on many diverse
platforms makes such "magic" possible.

The ability to dynamically download and run Java code over the Net has led some computer
pundits to proclaim that the age of truly component-based software development may actually have
arrived. The idea is that instead of buying huge monolithic word processing behemoths with
hundreds of obscure features that most users will never need, users can instead "create" a personal
word processor on the fly out of Java building blocks. This modern sort of programming is akin to
building a large toy ship out of Legos blocks. Or, more realistically, the process of creating a
component-based software product could be likened to building a highway bridge out of
standardized structural components.
Sun is advocating a Java component architecture called JavaBeans. A number of companies are
creating sets of JavaBeans for various purposes. If these efforts are successful, developers will be
able to create programs by putting together sets of prefabricated Beans as illustrated in Figure 1.1.
Microsoft's Component Object Model (COM) is very much oriented this way, although it is not
specifically designed to use Java. Component-based software has its own interesting security
implications and open questions. For example, how can the developer of a system trust a
component manufacturer not to have (purposefully or accidentally) introduced security holes into
the system? How can a component manufacturer anticipate all uses to which a component will be
put? And so on. These sorts of questions are the topic of current research, including some by the
authors of this book.

Figure 1.1 Component-based software allows a


designer to create large applications from
standardized building-blocks.

Components in Java are known as JavaBeans. The


idea of using pre-fabricated components to build large-
scale applications will likely do for software what the
Industrial Revolution did for manufacturing.

Thinking even farther into the future, one can imagine a fundamentally new kind of computer
document that contains the word processing, spreadsheet, and database software that was used to
create it. Using a document's embedded components, a writer or editor could modify the document
on any platform. The built-in components would allow different people using different machines to
edit the document without worrying about the kind of computer they are using or file type
compatibility issues. If Java is developed to its full potential, this future world may not be far off.

The new idea behind all of these exciting aspects of Java is simple: the ability to send data that can
be automatically executed wherever it arrives, anywhere on the Net. Java is an implementation of
executable content, or mobile code. This powerful idea opens up many new possibilities on the
World Wide Web. For the first time it is possible to have users download from the Web and locally
run a program written in a truly common programming language.

These features of Java are certainly exciting; however, Java's fantastic potential is mitigated by
serious security concerns. Security is always an issue when computers are networked. Realistically
speaking, no computer system is 100-percent secure. Users of networked computers must weigh the
benefits of being connected to the world against the risks that they incur simply by connecting. In
practice, the goal of a security policy is to make such tradeoffs wisely.

One of the key selling points of Java is its use as a "cross-platform" language for creating
executable content in the highly interconnected world of the Internet. Simply by using a Web
browser, a Web surfer can take advantage of Java's cross-platform capability. Of course, the
activity of locally running code created and compiled somewhere else has important security
implications. These implications are one focus of this book.

The same risks and benefits that apply to connecting to the Internet itself directly apply to using the
Java language. As you will see, these concerns become particularly critical when "surfing the
Web." The same technology that allows Java applets to enliven once-static Web pages also allows
unscrupulous applet designers to invade an unsuspecting Java user's machine. With Java applets
showing up everywhere, and many millions of people using Java-enabled browsers, it pays to know
where you are pointing your browser.

CHAPTER SECTION:3

Section 3 -- The Power of Networking


Networking has changed the face of computing. We once thought of computers as calculating
machines, but now most people rightly view them primarily as communication tools. An Internet
connection is as essential a part of today's computer as a disk drive. The move toward a globally
networked world has been significantly furthered by Java.

The Internet: A World of Connections

Since its birth in the early 1970s as a 12-node network called the ARPANET,3 the Internet has
exponentially exploded into a worldwide network that provides a central piece of the planet's
information infrastructure. Figure 1.2 shows the growth pattern of the Internet from its humble 12-
host beginning through today's some 30-million registered addresses.
Figure 1.2 Growth of the Internet since its early days as the ARPANET.

Data is from Network Wizards (www.nw.com). The Internet continues to grow at an astounding
rate.

Connecting computers together in a network allows computer users to share data, programs, and
each others' computational resources. Once a computer is put on a network, it is possible to access a
remote machine in order to retrieve data or to use its CPU cycles and other resources. Along with
this ability comes concern about security. Computer security specialists worry about issues such as:

• Who is allowed to connect to a particular machine


• How to determine whether access credentials are being faked
• Who can access which resources on a shared machine
• How to protect data (especially in transit) using encryption
• How and where to collect and store audit trails

Whenever machines are networked, these concerns must be addressed.

The Internet, the world's largest network of machines, has encouraged research into these security
issues. Mechanisms now in place go beyond simple password authentication, to firewalls and
security checking tools such as SATAN, ISS, and Ballista. New ideas in computer security are
constantly becoming available on the Net. Security approaches currently in preliminary use include
encryption-based authentication, encrypted communications, and intrusion detection based on
Artificial Intelligence (AI) [Hughes, 1995; Garfinkel and Spafford, 1996; Ghosh, 1998]. Computer
security has recently matured into a substantial commercial enterprise as well. As in any new field,
however, there is as much hype as there are barrels of snake oil. If it sounds too good to be true, it
probably is. Buyer beware.
The Web: Making the Internet Enticing

One of the driving forces behind the exponential growth of the Internet in the last several years has
been the introduction of the World Wide Web. In 1992, Tim Berners-Lee, a British researcher at
the CERN physics facility in Europe, invented the Web, a new way to use the Internet. His
invention introduced hypertext markup language (HTML) and Web browsing to the world. In 1993,
Marc Andreessen helped to write the Mosaic Web browser while affiliated with the National Center
for Supercomputer Applications (NCSA). He later cofounded the company now known as Netscape
Communications. Though it may be hard to believe, the Web is only a few years old.

Before the invention of the Web, the Internet was almost exclusively text based. Researchers used
it to transfer files to one another and to keep in touch via email. After the Web was invented, it
suddenly became possible to see graphical pages sent across the Net by Web servers. These Web
pages can include pictures, sound, video, and text, as well as hyperlinks to related pages. A Web
browser provides an easy-to-use, intuitive interface for "surfing," or traveling around the Web,
visiting other people's pages. Figure 1.3 shows how a typical Web page looks when viewed with
the Netscape browser.

Figure 1.3 A view of this book's companion Web site


(www.securingjava.com) as displayed by Netscape Communicator.

All current Web browsers include the capability of running mobile code automatically.

Ease of use is partially responsible for the astonishing numbers of Web users, and perhaps for the
sense of safety that most Web users seem to enjoy. In addition, creating Web pages is a relatively
simple process. HTML editors like Netscape Navigator Gold and Microsoft FrontPage make the
job especially easy. Given one of these editors and a Web server, you have all the pieces you need
to create your own Web site. An alternative to using an HTML editor is to write HTML code
directly. Either way, this snazzy HTML facade makes the Internet more attractive than ever.

As shown in Figure 1.4, the Web has grown just as quickly as the Internet itself. The figure charts a
conservative estimate of the number of Web servers on the Net. It is these servers that allow people
to make Web pages available to everyone. The figure does not properly reflect the number of Web
pages that are out there, which some people number in the hundreds of millions. Keep in mind that
a server has the potential to serve hundreds or even thousands of pages for multiple users
simultaneously.

Figure 1.4 Growth of the World Wide Web, shown as the number of Web servers, since
its introduction in 1993.

Data from the Internet Society (www.isoc.org).

Java: Spicing Up the Web

HTML-based Web pages are certainly a big step up from using the obscure, text-based Unix
incantations of ftp, news, gopher, wais, and telnet to get around on the Net; however, they also have
a major drawback. Much like the page that you are reading now, Web pages are static. Wouldn't it
be better to have interactive Web pages that dynamically change themselves according to feedback
from a user? Wouldn't it be better to program your Web pages to accept input, compute results, and
then display them?

This sort of dynamic activity should ring a bell. After all, programming languages allow people to
program machines to do just these sorts of things. Why not make a programming language for the
Web?

That is the essence of Java. Java is a full-featured programming language that allows programmers
to compose executable content for the Web. The Java language is designed to be usable on all
platforms so that code can move from one machine to another and still work, regardless of the kind
of machine it ends up on. Cross-platform compatibility has always been a stumbling block in
previous attempts to create programming languages for executable content. Mobile code can only
truly be mobile if it can be executed on all platforms without porting and recompiling!

In order to allow Java to run on a computer, the administrator must first install a Java Virtual
Machine (JVM), or a browser that includes a Java VM. The JVM interprets Java instructions and
translates them into machine-specific instructions. This allows Java to be run on many different
types of machines.4 For old timers, the whole idea is reminiscent of P-code from the 1970s.

Having a well-defined, platform-independent definition allows Java to get around problems that
have plagued the C programming language, making C less platform independent than its designers
intended. Unlike C programs, Java programs are not hampered by machine-dependent structures
such as:

• Byte ordering (low or high endian)


• Pointer size (16 or 32 bit)
• Integer size (16 bit, 32 bit, or 64 bit)

Java's careful definition shields it from these platform-specific elements of programming. Each
Java VM is written to a specific platform and translates the more generic Java instructions into
platform-specific instructions.

Java has upped the ante on the Web. The best Web pages now include Java applets that do
everything from displaying selectable news tickers to providing front-end graphical user interfaces
(GUIs) for internal databases. There are even some Web-based videogames written in Java. Java
applets have become commonplace.

The Promise of Java

Java is by far the most popular implementation of Web-based mobile code. Lesser-known
competitors include JavaScript, Safe-Tcl, Telescript, Word macros, Excel macros, ActiveX, and
Postscript. Each of these systems raises its own security issues. Any document-embedded scripting
language that can be transferred around the Net and run on different machines falls under the
classification of executable content.5 Propelled by the marketing powers of Sun Microsystems and
IBM, the Java wave is still building. Java avoids the interactive content limitations that were built
in to forms and CGI (Common Gateway Interface) scripts.6 Java's power lies in the ability to
program complete applications in a real programming language that can then be dynamically
distributed and run by virtually any user over the Web.

CHAPTER SECTION: 4

Section 4 -- Downloading Code: Not a New Problem


In the early days of the Internet, everyone agreed that downloading arbitrary binaries and executing
them on your machine was a bad idea. Of course, most people did it anyway. By the mid 1980s,
there was a lot of freeware and shareware out there to be downloaded. To find it, you could use
archie, which provided a way to search a large index of anonymous ftp content. Once you dug up
some leads (often several ASCII pages worth), you chose your target and ftp'ed what you needed.
Then you installed and ran it.

The risks of running some random person's downloaded-from-the-Net code on your machine are
clear. If the code has a virus attached, your machine can be infected. If the program is a Trojan
Horse that appears to be doing something useful while it is actually doing something nefarious,
your machine can become "owned" by someone else. This is especially dangerous for machines
connected to the Net. How can we be sure that a program that someone says is useful hasn't been
hijacked to do something nasty?

When it works flawlessly, the Java security model provides one possible answer to this question, as
it was designed to allow untrusted programs to be run on a computer safely. As we will see, the
base Java security model is meant to counter the threat of viruses and other forms of attacks. But in
the early days of the Net, Java did not yet exist. (To be completely accurate, Java was evolving in
the early 1990s from an embedded platform called Oak that was meant to be used for smart devices
like that Internet-enabled toaster you've heard so much about.)

Back to our history . . . The question in the late 1980s was, how could a user be sure that a program
had not been hijacked (or Trojan'ed)? Checksumming provided part of the answer. A checksum is a
simple computation performed on a piece of code to provide a digest, or "thumbprint," of a
program. (Combine this with digital signatures and you have a system that can provide both data
integrity and authentication, which is most desirable, as we will discover in Chapter 3.)

Not many people were into checksums back then, but they existed for at least a few anonymously
downloadable programs. Of course, who was to say that a program's checksum hadn't been
tampered with? In reality, most people either ignored the risks or chose to live with them.

Skipping the advent of gopher, which most people pretty much ignored anyway, the next big thing
was the Web. As discussed in the last section, the Web got its start in 1992. At first, the Web was
static. Java changed all that, making it possible for a Web server to provide programs as content.
Java applets are these programs. The dangers of mobile code and systems for addressing these
dangers are the focus of this book. But there's still a drawback, even with the power that Java adds
to the Web-the only way to tell when new content has been added to a Web page is to surf back
over and find out. That's where push technology comes in.

Push: Too Much of a Good Thing?

As if surfing the Web with a Java-enabled browser isn't bad enough security-wise, another new step
in mobile code delivery appears to be "push" technology. Push provides a way to have information
(including mobile code) automatically flow to your machine-without you even asking for it! (Well,
you do have to set things up once in the beginning, but after that, no more clicking.) Now the
inconvenience of clicking on a hyperlink is completely removed. Heck, you don't need to make any
decisions at all. Just sit back and watch the content (which may include Java applets, ActiveX
controls, and client-side scripts) come to you. With push it is possible to subscribe to "channels"
that do things like provide constant stock information, news headlines, and (most dangerously of
all) software updates.

There are many push systems out there. Two of the most popular are Castanet by startup Marimba,
and PointCast by PointCast, Inc. The security systems of Castanet and PointCast are briefly
covered in an article written by McGraw entitled "Don't Push Me: The Security Implications of
Push," which is available at www.developer.com/techfocus/123097_pushsec.html. Push channels
are now available in both Internet Explorer 4.0 and Netscape Communicator.

First off, push is not very well named. It should actually be called "timed pull." Most systems,
including PointCast, work by having a tuner program, which functions like a fancy browser, issue
HTTP requests for information from a push server. (This is the "pull" part.) Once requested, the
information comes back across the Internet as HTML-based HTTP traffic and is eventually
displayed in a special window. PointCast is set up to take over the screen when the computer is not
in use, much like a screensaver program. Every once in a while, the program will wake up and
check for new information, which is grabbed in chunks and sometimes cached. (This is the "timed"
part.)

Let's get this straight: It is still a really bad idea to download and run arbitrary binaries off the
Internet. Automating things so that this happens more easily, behind the scenes, doesn't serve to
make it any less dangerous. We've gone from having to request binaries through the text-based ftp
interface and install them, through clicking on a hyperlink (the Java model), all the way to having
content come to you.

In the meantime, security issues have yet to be properly addressed. How do you know that the
information a push server is sending you is secure? How do you know that the update that was just
pushed onto your PC is really from the company that developed the software? These questions are
familiar ones to people interested in security. What we need to make push systems safe is strong
authentication, foolproof data integrity, and trust in the broadcasters. Current push systems are only
beginning to address security concerns.
CHAPTER SECTION:5

Section 5 -- Java in a Demitasse


The security concerns raised in this book apply equally to both Java users and Java developers.
Using Java is as easy as surfing the Web. The simple use of Netscape Navigator, Internet Explorer,
or any other Java-enabled browser to run Java applets is a risky activity. In order to really
understand these risks, it is important to gain a deeper understanding of how Java really works.
Here is a short but thorough introduction to the Java language.

The Java development environment comprises three major components:

1. A programming language that compiles into an intermediate, architecturally neutral format


called byte code
2. The Java Virtual Machine that executes the byte code
3. An execution environment that runs on the JVM and provides some base classes useful for
building complete applications

Figure 1.5 shows how these three parts of the Java environment work together to provide
executable content for the Web. The Java Developers' Kit (JDK) is provided free to all. It includes
the three parts of the Java environment outlined here. To get your own copy, point your browser to
URL java.sun.com.

Figure 1.5 How Java implements the original sandbox approach to


mobile code.

Java source code is compiled into Java byte code which is transferred across
the Web to the browser that requested it. The HTML in a Web page specifies
which code is to be fetched from the Web server. The requesting Web
browser, prompted into action when a user clicks on a hyperlink, (1) fetches
the code from the Web, (2) verifies it, (3) instantiates it as a class or set of
classes in a namespace. The applet executes and (4) invokes a dangerous
method (5) causing the Security Manager to be consulted before the method
runs. The Security Manager (6) performs runtime checks base on the calling
class's origin and may veto some activities.

Because Java byte code runs on the Java Virtual Machine, it is possible to run Java code on any
platform to which the JVM has been ported. Some Web browsers, such as Netscape and Internet
Explorer, include an encapsulated version of the JVM. Using their built-in VMs, such Java-ready
browsers can automatically download and execute Java applets when a user accesses an HTML
Web page including the <APPLET> tag.
The Java Language

One of the first public introductions to Java came in the form of a whitepaper released by Sun (and
since updated many times) [Sun Microsystems, 1995]. An especially pithy sentence from that
document attempts to describe the fundamental aspects of Java all at once. It reads:

Java: A simple, object-oriented, distributed, interpreted, robust, secure, architecture neutral,


portable, high-performance, multi-threaded, and dynamic language. Quite a collection of
buzzwords. In fact, some people joke that Java is "buzzword compliant." This book is concerned
mostly with the security claim, of course, but in order to understand the implications of Java for
computer security, you need to grasp the other important characteristics of the language first.
As the quote claims, Java has many interesting features. They will be briefly introduced here.
Pointers to more information on Java can be found on page 31. The Java language is:
Object-oriented: Unlike C++, which is an objectivized version of C, Java is intrinsically object-
oriented. This changes the focus of programming from the old procedural way of thinking (as in C
and Pascal) to a new data-centric model. In this new model, data objects possess associated
methods. Methods perform actions on data objects. Every Java program is composed of one or
more classes. Classes are collections of data objects and the methods that manipulate these data
objects. Each class is one kind of object. Classes are arranged in a hierarchy such that a subclass
inherits behavior and structure from its superclass. Object-oriented languages were designed using
the physical world as a metaphor. Classes communicate with each other in much the same way that
real physical objects in the world interact.

Strongly typed: This means that a Java program cannot arbitrarily access the host computer's
memory. Memory access by Java programs is limited to specific, controlled areas having particular
representations. Type safety is verified when code is loaded into the JVM by the Byte Code
Verifier (see Chapter 2, "The Base Java Security Model: The Original Applet Sandbox"). In
addition, runtime checks on type safety (such as checks for array bound overflow, type
incompatibility, and local-versus-remote code security policy) are all handled by the Java Virtual
Machine. As we shall see, type safety is essential for Java security. In fact, a majority of serious
Java security attacks target the type system.

Multi-threaded: Java programs can execute more than one task at the same time. For example, a
multimedia Java applet may want to play a sound file, display a picture, and download a file all at
once. Since Java is multi-threaded, it supports the concurrent execution of many lightweight
processes. An obvious benefit of this capability is that it improves the performance of multimedia
applications at the user end. Java's built-in support for threads makes designing such applications
far easier than it is in C and C++. Primitives for synchronization are also provided in Java.

Java has other important characteristics adapted from modern programming languages such as
Scheme (a popular dialect of Lisp) and ML. In particular, Java uses:
Garbage collection: Memory management is usually handled in one of two ways. The old-
fashioned approach is to have a program allocate and deallocate memory itself. This approach
allows all sorts of insidious errors and hard-to-squash bugs. C, for instance, uses this method. By
contrast, Lisp introduced the modern concept of garbage collection in 1959! Garbage collection
requires the system (rather than the programmer) to keep track of memory usage, providing a way
to reference objects. When items are no longer needed, the memory where they live is
automatically freed so it is available for other uses. Java provides a garbage collector that uses a
low-priority thread to run silently in the background. Java's memory management approach has
important implications for the security model since it prevents problems associated with dangling
pointers.

No pointers: This is also a feature of Java's modern memory management scheme. Instead of
allowing access to memory through pointers, memory is managed by reference. The crucial
difference between references and pointers is that references cannot be manipulated through
arithmetical means (as can pointers). This eliminates many potential bugs. Pointers are one of the
most bug-prone aspects of C and C++. Eliminating pointers has the effect of making Java a much
more reliable and safer language.

Exception handling: This defines how the program will manage an error condition. For example,
if a Java program tries to open a file that it has no privilege to read, an exception will be thrown.
Exception throwing and catching is a system for gracefully managing runtime errors that might
otherwise crash a system. This is a good idea if you are concerned about security.

Dynamic linking: Software modules (classes in Java) are linked together as they are needed. The
Java language knows where it should look for classes that need to be linked while a Java program
runs. By contrast, C has a linking phase during which all needed constructs are linked before the
program is run. The linking phase in C is static since library functions are assembled together with
other code into a complete executable at compile time. Dynamic linking makes it easier to keep
Java programs up-to-date since the latest version of a class will always be used. This can turn out to
be a problem for programs that expect a class to behave the way it has in the past and are surprised
when a new version appears. Version control and software assurance become much more
complicated with dynamic linking too. Java finds classes that it needs by searching for them in
locations specified in the CLASSPATH environment variable (though the system is undergoing
revision for Java 2). (As we will discuss in Chapter 2, it turns out to be very hard to ensure type
safety when dynamic class loading is allowed.)

Though it has more than doubled in size since its original introduction, Java is still a relatively
simple language. This is especially apparent when Java is compared with C and C++ [Daconta,
1996]. In C, there are often many possible ways in which to do the same thing. Java tries to provide
only one language mechanism with which to perform a particular task. Also, Java provides no
macro support. Although some programmers like using macros, macros often end up making
programs much harder to read and debug.

The designers of Java made their language simple by removing a number of features that can be
found in C and C++. Things that were removed include the goto statement, the use of header files,
the struct and union constructs, operator overloading, and multiple inheritance. Together with the
elimination of pointers, removal of these aspects of C and C++ makes Java easier to use. This
should result in more reliable code.7

We will revisit the impact that Java's features as a language have on security in Chapter 2.
Portable Byte Code and the Java Virtual Machine

The second major component of the Java development environment is the Java Virtual Machine.
The VM makes Java's cross-platform capabilities possible. In order to run Java byte code on a new
platform, all that is required is a working VM. Once the VM has been ported to a platform, all Java
byte code should run properly.

Making a byte code/VM pair that works well on many varied platforms involves setting a few
things in stone. Java has variables that are of fixed size and of fixed format. An integer in Java is
always 32 bits, no matter what the word size of the machine running Java. Making data formats
machine independent and compiler independent is crucial to making Java truly portable. The very
different way in which variables are managed on different C platforms causes no end of portability
problems for C programmers.

The VM also makes use of symbolic data stored inside of Java byte code files. Java byte code
contains enough symbolic information to allow some analysis of the byte code before it is run. This
is one way the Java environment ensures that Java's language rules have been followed by the
compiler-something critical to security. Rules checked include, for example, type safety rules, and
ensuring that certain things claiming to be of a certain type actually are of that type. Since the Java
byte code Verifier is a critical part of the security model, it is discussed in detail in Chapter 2.

Using a Virtual Machine has obvious important repercussions for the Java approach. The VM
makes portability possible, and it helps to ensure some of Java's security features. Since Java is
often implemented using an interpreter, speed can be an issue. Interpreted languages are inherently
slow because each command must be translated to native machine code before it can be run. With a
compiler, this work is all done ahead of time, when an executable is created for some particular
platform. Without just-in-time (JIT) and hotspot compilers, Java's interpreted code is about 20
times slower than native C code. When this new technology is used, Java speeds begin to approach
native C.

Reusable Class Modules

The third part of the Java development environment is a set of predefined classes that implement
basic functionality. The "personal" version of the JDK includes, for example, an Abstract
Windowing Toolkit (AWT). These classes provide a set of graphical user interface (GUI) tools for
creating windows, dialogue boxes, scrollbars, buttons, and so forth. Java also includes classes for
full network support that provide application program interfaces (APIs) for sockets, streams, URLs,
and datagrams. A POSIX-like I/O system with APIs for files, streams, and pipes makes the
environment comfortable for experienced Unix programmers. Classes are grouped together into
packages according to their functionality. Table 1.1 lists the packages included in the Java
Developers' Kit (JDK) version 1.1. Note that Java's core classes have grown significantly in the last
few years.
PACKAGE DESCRIPTION

java.applet The applet class.

Abstract Windowing Toolkit: The AWT provides graphics, GUI


java.awt components, and layout managers. A new event model was introduced
with JDK 1.1.

Inter-application data transfer support, including clipboard cut-and-


java.awt.datatransfer
paste.

java.awt.event Classes and interfaces for the new AWT event handler.

java.awt.image Image processing classes.

java.awt.peer Interface definitions for GUI components and platforms.

java.beans The JavaBeans API for creating reusable software components.

java.io Input/output classes: A relatively large number of classes for I/O.

Central Java language classes: Defines Object, Class, and primitive


java.lang
types.

Classes that allow a Java program to examine Java classes and to


java.lang.reflect
"reflect" on its own structure.

Two classes that support arithmetic on arbitrary-size integers and


java.math
arbitrary-precision floating-point numbers (important for cryptography).

java.net Networking classes.

java.rmi Classes and interfaces for Remote Method Invocation.

java.rmi.dgc Distributed garbage collection.

Classes and interfaces for tracking, naming, and advertising remote


java.rmi.registry
objects.

java.rmi.server The heart of the RMI system.

Classes and interfaces that define fundamental cryptographic services.


java.security
(See Chapter 3.)

java.security.acl Access control list interfaces.


Interfaces required for the Java Security API's implementation-
java.security.interfaces
independent design.

java.sql Java Database Connectivity (JDBC) API.

java.text Classes and interfaces for internationalization.

Miscellaneous but critical classes. These classes are required for many
java.util
others.

java.util.zip Classes for manipulating zlib, ZIP, and GZIP file formats.

The predefined Java classes provide enough functionality to write full-fledged programs in Java.
Using the predefined classes as primitives, it is possible to construct higher-level classes and
packages. Many such home grown packages are available both commercially and for free on the
Net.

The World of Java Applications

In the early days of Java's popularity, most Java programs took the form of applets, small programs
that were attached to Web pages and loaded and run in Web browsers. As Java developed, people
began to write substantial applications in Java, using it simply as an improved version of traditional
languages such as C.

Java has always been good for more than writing applets, and the world is now catching on to that
fact. Java is really a good platform for any application that needs to be extended or customized,
perhaps across the network, after it is deployed. A browser is only one example of such an
application.

Another increasingly popular use of Java is in Web servers. Many servers have extension
mechanisms, but the Java Servlet API provides a particularly flexible and compelling vehicle for
extending a server with new application-specific or site-specific functions. Most major Web servers
now support the Java Servlet API. Compared to browsers, servers present more difficult security
challenges, since servers have more stringent reliability requirements and store more valuable data.

Java's features also make it a good platform for creating new server-type applications. With natural
support for multithreading, database access, and networking, Java gives developers a natural leg up
in designing such applications. For these reasons, Java is being used increasingly in enterprise
computing.

One common structure for such systems uses a "three-tier" architecture. A traditional database
server acts as the "back end" tier, storing and managing the data needed to support a business
application. The middle tier is a Java-enabled specialized server that interacts with the database and
implements the "business logic" needed to manage client interactions with the system. The "front
end" tier is a Java applet that runs in the client's Web browser and provides a convenient user
interface so that users can interact naturally with the system. Three-tier systems put together several
uses of Java and, as a result, face a wide array of security issues.

In addition to all of these applications in traditional computers, Java is being deployed in embedded
devices such as smart cards, key rings, and pagers. Embedded applications are often involved in
electronic commerce systems, adding yet another series of twists to our security story.

The growing variety of applications is reflected in the subject matter of this book. While the first
edition focused almost exclusively on applet security issues, this edition encompasses the full
breadth of today's Java applications. We want to provide you with the information you need to
know to maintain security while building, deploying, managing, and using up-to-date, Java-based
systems. As Java has gotten down to business, so has this book.

Trust, Applets, and Applications

Java is much more than simply a language for creating applets. In the early days of Java (less than a
handful of years ago), it was important to distinguish applet code (which was typically treated as
untrusted and relegated to the sandbox) and application code (which was typically treated as fully
trusted built-in code). This distinction is no longer a useful one.

An alternative way to carve up the Java program space is to think about code in terms of levels of
trust. Programs that are more trusted can be allowed to carry out potentially dangerous acts (like
writing files). Programs that are less trusted will have their powers and permissions curtailed.

If we think about Java programs this way, it is still possible to make sense of the old distinction
between applets and applications. Java applets are usually, though not necessarily, small programs
meant to be run in the context of a Web browser. Obviously, applets involve the most client-side
(or user) security concerns of any Java programs. In fact, Java's security policies originally existed
in order to make applets feasible. The Java runtime enforces severe limitations on the things that
applet classes may do [McGraw and Felten, 1996]. See www.javasoft.com/sfaq and Chapter 2 for
details. In terms of the new trust-based distinction, applets are clearly treated as untrusted. This
makes sense, since the origin of an applet is often unknown or unfamiliar.

In the early days of Java, Java applications had no such restrictions. In terms of our trust
distinction, applications in Java before Java 2 were treated as completely trusted code. That meant
applications could use the complete power of Java, including potentially dangerous functionality.

The reason the old distinction between applets and applications no longer makes sense is that today,
applets can be fully trusted and applications can be completely untrusted. (Note the use of the word
can in the previous sentence; we don't mean to say that applets are always trusted or that
applications are never trusted.) In fact, depending on the situation, each and every Java program
can be trusted, partially trusted, or untrusted. Sound complicated? That's because it is.
With the introduction of Java 2, Java includes the ability to create and manage security policies that
treat programs according to their trust level. The mechanisms making up the base sandbox are still
under there somewhere, but they serve merely as a default situation to handle code that warrants no
trust. The interesting thing is that code that is partially trusted can be placed in a specially
constructed custom sandbox. That means a partially trusted applet can be allowed to, say, read and
write a particular file or make a network connection to a particular server. This is good news for
Java developers who were chafing under the constraints of the restrictive original sandbox.

Figure 1.6 illustrates the way in which the old applet/application distinction can be recast in terms
of black-and-white trust. It also shows the impact that Java 2 has on the black-and-white trust
model, transforming it into a shades-of-gray trust model.

Figure 1.6 From black-and-white to shades-of-gray.

The distinction between applets and applications found to be useful during the JDK
1.0.2 days no longer applies to mobile Java code based on the Java 2 model. In
fact, all along the real distinction behind the scenes was between fully trusted code
and fully untrusted code. A black-and-white distinction between trusted code and
untrusted code underlies both JDK 1.0.2 and JDK 1.1. By contrast, the Java 2
approach to trust management implements a policy-oriented shades-of-gray
architecture. Under Java 2, code can be constrained or unconstrained regardless
of whether it is applet or application code.

The Many Flavors of Java

Currently, a large and growing number of Java systems are 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. This book discusses security risks that apply to all
flavors of Java, but will focus on Java 2 and Card Java 2.0.

Counterintuitively, Java is both growing and shrinking at the same time. The JDK, now up to Java
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. Java 2 involves fundamental changes to the Java security model as the Java
sandbox is metamorphosing 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.

All of Java's built-in security functionality, including the recently added authentication and
encryption features (which began to appear with JDK 1.1), are available to Java application
developers. This functionality makes it possible for an application to establish its own security
policy. In fact, Java-enabled browsers do just that, determining the security policy by which all
applets that run inside them must abide. For obvious reasons, an applet is not allowed to change the
browser's (or for that matter, any application's) security model!

CHAPTER SECTION:6

Section 6 -- Securing Java


Security risks fall into four basic categories: system modification, invasion of privacy, denial of
service, and antagonism. These four categories of risk are discussed in detail in Chapter 2. The first
two of our risk categories are handled moderately well by Java; the second two are not. Risks are
particularly egregious in Java since exploiting vulnerabilities is simply a matter of booby-trapping a
Web page with a malicious applet or two. Chapter 4, "Malicious Applets: Avoiding a Common
Nuisance," and Chapter 5, "Attack Applets: Exploiting Holes in the Security Model," discuss two
distinct forms of hostile applets. Java applets with bad intentions-exploit scripts-are the equivalent
of every security administrator's nightmare [Garfinkel and Spafford, 1996].

Java's designers are well aware of many of the risks associated with mobile code. 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 naïve users (most of the millions of Netscape Navigator and
Internet Explorer users) would not have to become security experts just to surf the Web.

In its default form, Java presents a multitiered approach to security. At a general level, the tiers
include:

• Restricted access to file systems and the network


• Restricted access to browser internals
• A set of load time and runtime checks to verify that byte code is following the rules
• A system for signing code and assigning it some level of capability

The Java security model will be detailed in Chapter 2 and Chapter 3. Many claims have been made
about the security of the Java language. We will try to separate reality from marketing hype in
order to better understand the Java security model. Java also provides a set of tools with which a
developer can produce security-critical code (for both applets and applications). In addition to a
number of advanced language features like array bounds checking and byte code validation, Java
provides:
• A set of cryptographic APIs for standard algorithms
• Cryptography engines that provide the guts for a small subset of the APIs
• A strong, stack-based security system

Although this book is not just a guide to Java's security APIs, we will discuss Java's security
functionality in detail. In particular, we will emphasize that no computer language as powerful as
Java makes writing security-critical code automatic or easy.

CHAPTER SECTION:7

Section 7 -- How Does Java Security Stack Up?


As we have mentioned, Java is not the only game in town when it comes to mobile code. Other
mobile code systems include JavaScript, Safe-Tcl, Telescript, Word macros, Excel macros,
ActiveX, and Postscript. Of these systems, the one most often touted as a direct competitor to Java
is Microsoft's ActiveX (sometimes called DNA depending on the whim of Microsoft marketeers).
So what does ActiveX do for security, and how does it compare with Java's approach? Besides
ActiveX, what other mobile code systems present security risks?

ActiveX Security Issues

The first thing to know about ActiveX is that it does not have an enforcement-related 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?

Sandboxes and Signatures


There are two major approaches addressing the security concerns raised by mobile code systems:
sandboxing and code signing. The first of these approaches, sandboxing, is an idea embraced by
early implementations of Java (say, JDK 1.0.2). We extensively cover the Java sandbox in Chapter
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 files,
such as 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 that
they vouch for. It is important to stress the fact that code signing is completely a matter of trust;
there is no enforcement mechanism protecting you once you decide to trust a piece of code. The
trust model implements authentication and authorization. What this means is that there is no such
thing as ActiveX security enforcement! That's not to say signature-based trust models are not
useful. They are. In fact, trust models will play an integral role in future security models for mobile
code. Much more detail on code signing, especially as it relates to Java, is found in Chapter 3,
"Beyond The Sandbox: Signed Code and Java 2."

Code Signing and ActiveX


ActiveX is a high-profile form of mobile code promoted by Microsoft. Note that in practice its
"mobility" is completely constrained to one platform, however. As it is actually used today,
ActiveX is language independent, but not platform independent, meaning that real ActiveX controls
work only on Microsoft's Win32 platform (Windows 95, Windows 98, and Windows NT).
Technically, these controls could be recompiled for other platforms, but virtually nobody currently
produces controls for non-Win32 platforms.

One caveat: Comparing ActiveX and Java is somewhat like comparing apples and oranges, even
though everyone 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 [Lewis, 1998].)

ActiveX has been roundly criticized by computer security professionals since 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 very difficult to forge. That means a program
like your browser can verify a signature, allowing you to be fairly certain who vouched for a piece
of code (as long as people are carefully guarding and managing the private keys used to sign code).
To make things easy, 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 modified into a Trojan Horse.

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 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 (although their use is not widespread). For an explanation of these attacks and more on
ActiveX insecurity, see Anup Ghosh's book E-Commerce Security: Weak Links, Best Defenses
[Ghosh, 1998].
Sandboxes versus Signatures
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 Java 2 security. That means in Java, you get
everything that ActiveX is doing for security plus the ability to run untrusted code fairly safely.

Another significant factor is that the sandbox approach is more robust in the face of accidental bugs
in mobile programs. Even if the sandbox isn't bulletproof, it will most likely prevent a bug in a
mobile program from trouncing important data or programs by mistake.

As we shall see in Chapter 3, 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.

Microsoft's Authenticode and Security Zones


When a signed ActiveX control is downloaded, the browser detaches the signature block (which is
a signed one-way hash of the control packaged together with a standard X.509 certificate issued by
a certificate authority) and performs checks on the identity of the signer using Authenticode. This is
a two-step process. First the certificate is examined by checking the certificate authority's identity.
Then the one-way hash is checked to ensure that the same code that was signed was the code that
arrived. Note that these checks say nothing at all about whether a control will or will not behave
maliciously. They only check the identity of the signer and that the code has not changed since
signing.

Microsoft Internet Explorer 4.x implements a security zone concept meant to ease the management
of security policies for signed content such as ActiveX controls and Java applets. The system
organizes Web sites into four "zones of trust" (or more if you customize): Local intranet zone,
Trusted sites zone, Internet zone, and Restricted sites zone. Each zone can be configured with
security levels of: High (most secure), Medium (more secure), Low, or Custom. The idea is to
divide Web sites into these zones and assign the zones varying levels of trust.

Figure 1.7 shows a dialog box from Microsoft Internet Explorer (MSIE) that allows a user to
manage Authenticode security zones.
Figure 1.7 Authenticode's signature-based trust model
implements the concept of security zones in order to aid in
managing mobile code.

Microsoft Internet Explorer provides a dialog box that users can


access to manage security zones. Though the importance of powerful
policy management tools cannot be overstated, some security
professionals complain that allowing a user to set security levels is
not a good idea-especially if high security correlates with high level of
annoyance (through implementing, for example, too many security
queries).

Zones are a useful tool that can help make a security policy more coherent. The concept may be
particularly useful in non black-and-white policy situations currently beyond the scope of ActiveX.
We think security zones are a useful tool that Java security systems beyond Microsoft's should
support as well.

In ActiveX with security zones, the security policy itself remains black and white: A mobile
program is either fully privileged or completely banned from the system. Since most users are
inclined to run cool-sounding code just to check it out regardless of the risk, popping a dialog box
in front of a user and requiring an instant security decision is not a good idea. As one of the authors
(Felten) is known to say, "Given a choice between dancing pigs and security, users will pick
dancing pigs every time." The Princeton team correctly warns that relying completely on a human-
judgment-based approach to security in not likely to be as successful as blending judgment with
technology-based enforcement, as newer Java systems do. See Figure 1.8.
Figure 1.8 Given the choice between dancing pigs and security, the world will pick dancing pigs
every time."

The dancing pigs applet, available through the book's Web site (www.securingjava.com), demonstrates
the use of digital signatures in Java. See Appendix C.

One way in which Authenticode addresses this problem is to put the security decisions in the hands
of a system administrator. Using the MSIE Administration Kit (IEAK), an administrator can
preinstall a list of permitted certificates and block the installation of others. This is a step toward
centralizing security policy management (which is something most corporate users demand).
However, in the end, the ActiveX model is still only a trust model. Just for the record, Netscape
now includes a similar site-wide policy administration system.

We discuss these issues of trust, identity, and signatures again in more detail in Chapter 3, though
the focus is on Java and not ActiveX.

More on ActiveX Security


The Princeton Team has written an FAQ, reprinted in Appendix A, called Security Tradeoffs: Java
versus ActiveX, in which a number of common questions about Java and ActiveX are answered.
On the Web, the FAQ can be found at www.cs.princeton.edu/sip/java-vs-activex.html.

Two other good places to look are in Chapter 2 of E-Commerce Security by Anup Ghosh [Ghosh,
1998] and page 18 of Web Security Sourcebook by Avi Rubin, Dan Geer, and Marcus Ranum
[Rubin, Geer, and Ranum, 1997].

JavaScript

Another mobile code system is JavaScript (Microsoft's version is called JScript). Note that other
than the four letters-J, A, V, and A-JavaScript has nothing in common with Java. In the early days,
JavaScript was known as LiveConnect, but once the marketing folks at Netscape saw the Java wave
building, they decided to ride along. JavaScript allows code to be directly contained in HTML
documents themselves, code that can dynamically change the HTML that a Web user ultimately
sees through a browser.

JavaScript has its own security headaches. Though it is not an ultra-powerful scripting language,
JavaScript can easily be used to carry out denial of service and invasion of privacy attacks. Much
more discussion about denial of service is found in Chapter 2. JavaScript was used extensively in
the Princeton Team's Web Spoofing attack [Felten, et. al., 1997].

To find out more about JavaScript security, a good place to start is at John LoVerso's JavaScript
security site:www.camb.opengroup.org/~loverso/javascript/. On his JavaScript Problems I've
Discovered page, LoVerso describes JavaScript attacks that:

• Track a surfer's history, secretly keeping tabs on all sites visited by a user and reporting back
to a collection site
• Read directory listings, learning about a Web surfer's file system and reporting back to a
collection site
• Steal files, mailing the stolen goods back to an attacker
• Construct Java tags, circumventing systems that attempt to block Java applets by removing
the <APPLET> tag. (For more on why this approach to stopping Java applets is silly,
seeChapter 6.)

Make sure that your mobile code security policy (you have one, right?) addresses JavaScript as well
as Java.

What Does All This Have to Do with Java?

The important take-home message of this section is that Java security concerns do not exist in a
vacuum. If someone tells you that you should disable Java, but pays no attention to these other
threats, he or she is not doing you much of a favor. The truth is, much scarier things than Java are
out there. In fact, many of the attacks we have touched on here pale in comparison to security
concerns raised by a Windows 95 PC connected to the Internet. Try to put all of the security
concerns relevant to you on the same scale, and address the biggest risks first.

CHAPTER SECTION:8

Section 8 -- Where to Find More Information on Java


Java is growing rapidly, and keeping up with it requires as much energy as looking after a herd of
two-year-olds (believe us, we know). Keeping up with the edge is just as important for security
purposes as it is for any other. Here are some resources that can help make a time investment worth
it.
Java on the Web

An excellent place to start learning about Java is the Web itself. The first URL to check is JavaSoft
(java.sun.com). Also useful are developer.comand JavaWorld. MindQ sells a set of excellent CD-
ROMs that provides a multimedia introduction to programming Java applets and applications
(among other issues). See its Web page for details at www.mindq.com. (MindQ produced the
authors' Java Security CD-ROM as well.) To discover some of the many other Java resources on
the Net, search for Java at Yahoo! or on AltaVista. Also see two collections of security-related Java
links put together by the authors at www.securingjava.com and at www.cs.princeton.edu/sip. The
references section of this book includes a complete listing of all URLs cited throughout the book.

Java Books

The number of books on Java is growing almost as fast as the Web itself, and the Java shelf is
groaning under their combined weight. For a comprehensive list,
see lightyear.ncsa.uiuc.edu/~srp/java/javabooks.html. We have had a chance to use a few of them
as Java coders. Here are four, with a brief review for each:

Core Java Volume 1-Fundamentals [Horstmann and Cornell, 1997]. This is a good book; big,
but definitely useful. In fact, Core Java got so big that it split into two volumes for the JDK 1.1
edition. It is full of comparisons to C++ and Visual Basic, including useful pictures. The
authors provide implementations for other classes that are not in the Java libraries, but are
commonly used.

Inside the Java Virtual Machine Specification [Venners, 1997]. For anyone interested in the
inner workings of Java's Virtual Machine, this is the book to get. As we'll see, Java applet
security boils down to what byte code is allowed to do and how its behavior can be
constrained. That means that learning about how the VM does its thing is a useful exercise for
those people concerned about security.

Java in a Nutshell, second edition [Flanagan, 1997]. This book remains everyone's favorite
(well, every developer anyway), probably because it is so useful. O'Reilly is famous for its API
books and, true to form, this book provides an extensive API for the packages provided by
Java. This makes it excellent for a quick desk-side reference. There are some examples, but if
you learn best by examples, you should consider Java Examples in a Nutshell [Flanagan,
1997]. Both books are equally useful for beginners and more advanced Java programmers.

Java Network Programming [Hughes, Shoffner, and Winslow, 1997]. One of the best reasons
to use Java as a development platform is to take advantage of its built-in networking ability.
This excellent book is filled with hands-on examples that are included on a CD-ROM. Of
particular interest to security buffs, a number of cryptography algorithms are presented. Note
that there is an O'Reilly book of the same title (this one is from Manning); however, this is the
one to get.
Java Security Resources
We're glad to say the amount of information available on Java security is also growing. There are
both a number of books available and a large number of Web sites. On the Web, we provide the
most comprehensive and up-to-date hotlist-the Java Security Hotlist-at www.securingjava.com. The
hotlist, which has over 100 links divided into 9 categories, has been reproduced as Appendix B,
"The Java Security Hotlist." Of course URLs are notoriously dynamic, and Java security is a fast-
moving field. For the latest version of the hotlist, see the Web site.

The Secure Internet Programming Lab at Princeton also maintains a site with information on
security alerts and ongoing Java Security research atwww.cs.princeton.edu/sip/.

Java Security Books

For at least a year, the first edition of this book, Java Security: Hostile Applets, Holes, & Antidotes,
was the only available book on Java security. Since that time, a number of other books have come
out that address the topic. Of course, we are biased about which one is best, but we thought you
might appreciate our opinions about the others anyway: Java Security: Hostile Applets, Holes, &
Antidotes [McGraw and Felten, 1996]. The first book on Java security. This book was intended to
educate Web users about the risks of Java security. It includes a discussion of the base Java security
model and the original Java security holes. We're glad we wrote it.

Java Network Security [Macgregor, et al., 1998]. This book appeared in 1998 and includes
information on JDK 1.1, but nothing on Java 2. The book has a number of technical errors and
unintentionally misleading claims about security as well. If you want a copy of everything ever
written on Java security, get a copy; otherwise this one is skippable.

Java Security [Oaks, 1998]. O'Reilly is well known for its developer-oriented books. This book
fits the bill, as it provides both an API reference guide and a number of code samples. It is
almost up to date (the switch to the doPrivileged() API discussed in Chapter 3 is not covered
by Oaks) and carefully details Java 2 functionality. One caveat: Oaks is an employee of Sun
Microsystems and certainly toes the party line. The discussion of security risks and
implications reflects this fact. Also missing is any treatment of Java security holes.
Nevertheless, if you are a developer who wants to learn about the APIs and you don't care too
much about the bigger picture, this book is for you.

The Web Security Sourcebook [Rubin, Geer, and Ranum, 1997]. Although this is not a Java
security book per se, many of the lessons this book teaches are entirely relevant to people
interested in Java security. This is a practical, hands-on book that covers Web server security,
mobile code, CGI, and more, written by security experts of the highest caliber. E-Commerce
Security [Ghosh, 1998]. Java is often put to use in e-commerce systems, and of course, e-
commerce systems must take security very seriously. This book provides essential data for
securing your e-commerce system. It discusses common errors, real attack targets, and
solutions.

CHAPTER SECTION:9
Section 9 -- Mobile Code Has Its Price
Having programs embedded in Web pages that can run on any platform is an excellent idea. But in
order to get this power, users take a great deal of risk.

A Web surfer can click over to a Web page with an embedded applet that immediately and
automatically begins executing. Often, the user doesn't even know this is happening. This situation
might not be so bad if the Java environment being used were 100-percent secure. However, to make
Java really secure would require making it completely impotent.8

There is a price that must be paid for the power of executable content. This price is very similar to
the price that must be paid in order to connect to the Internet in the first place. (In fact, if you
decide Java security risks are too much to bear, you should ask yourself what you are doing
connected to the Internet at all!) The bill is payable in terms of risk and exposure to attack. The
question is, how much risk are you willing to take? How critical is the information on your
machine? Our goal in writing this book is to arm the reader with the data that are needed to make
an informed, intelligent decision about Java, both as a system for mobile code and as a
development platform.

Downloading Mystery Code

How often do you download executable code from various unknown sites on the Net? Do you think
about where the code is coming from and who wrote it? Do you know what it will do before you
run it?

Even if you are particularly cautious about downloading binaries from the Net, the answers to the
questions raised will undoubtedly soon change. Applets are cropping up everywhere. At the
moment, surfing the Web with a Java-enabled browser is tantamount to downloading and running
arbitrary binaries, albeit with some level of security provided by Java. Deciding whether this is a
good idea is an important decision that is as personal as a financial investment strategy.

It is worth repeating that there is no such thing as perfect security. This is true for any system on
the Internet, not just systems using Java. Someone will always be probing Java security, trying to
find new ways around or through the existing system. In the real world, all you can expect is
reasonable security. The solution to this conundrum is finding an acceptable tradeoff between
functionality and security.

Playing the Cost/Benefit Game

The Internet can be a dangerous playground. Java offers an intriguing approach to the problem of
security by neither ignoring it entirely (as most languages do) nor being completely paralyzed by it.
Deciding what level of risk to incur is really a matter of weighing the potential costs of using Java
against the clear benefits of using Java. Making an informed and intelligent decision requires
understanding both aspects of the situation. Business people are always weighing costs and benefits
when making complicated decisions. The same sort of careful consideration that goes into forming
a business plan should also go into the formulation of a Java use policy.

The Java hype machine has been exceptionally good at broadcasting the benefits of Java. It has
been successful largely because Java really does have vast potential. On the other hand, the
advertising has been slightly less straightforward about the risks. (To this day we hear claims that
Java is 100-percent secure, or that there is no need to worry about Java security.) This may be
because the risks are complicated and sometimes difficult to understand. Computer security is a
new field to many users, and few people are aware of all the issues. As Java applets become
ubiquitous, it behooves us to become more aware of security issues. Ignorance is not bliss.

CHAPTER SECTION:10

Section 10 -- Assessing the Risks


Now that the basics of the Java environment have been covered, you are ready to examine Java
security in earnest. It is only after understanding what the security model is, how it works, and how
it doesn't, that you can truly begin to assess the security situation.

People should think carefully about using Java even casually with a Java-enabled browser. This
book will present some of the facts associated with Java security so that you may decide when,
where, and how to use Java. Unfortunately, there is no black-and-white answer to the question:
How and when should I use Java?

The Base Java Security Model:


The Original Applet Sandbox
Java is designed so that programs can be dynamically loaded over the network and run locally. This
very powerful paradigm promises to change the face of computing as we know it. A browser that
can interpret Java byte code (such as Netscape Navigator or Internet Explorer) can download and
locally execute applets that are embedded in a Web page. This activity of downloading and
executing is completely automatic, requires no user approval, and sometimes occurs without the
user even knowing. Remember, by simply pointing your browser at a Web page containing an
applet, you start Java. Any applet started in this fashion is not required to advertise its presence.
More and more Java applets appear on the Web every day. Applets are becoming ubiquitous. This
means that surfing the Web with a Java-enabled browser is a more risky activity than surfing the
Web in the days before Java.
It is extremely unlikely that all users of Java-enabled browsers will consider the security
implications of surfing a site before each Web page access. If the mobile code paradigm is going to
work, security concerns should be addressed in the language of the content itself. That way, users
will not need to worry too much about security. Java's designers took this task to heart. One of their
fundamental concerns was making the use of Java transparent, automatic, and above all, safe. As a
result, Java was developed with key security issues in mind.

It is clear that the Java development environment attempts to address the security problems
introduced by the idea of dynamically downloading and running external, untrusted code. To what
extent Java succeeds in this task is a subject of debate. Security concerns have always been one of
the major technical stumbling blocks to achieving safe mobile code. Java took these concerns
seriously and made a good effort to protect Web users. In this chapter, we present the original (or
base) Java security model and discuss how it mitigates some of the risks that applets introduce.

The original Java security model presented in this chapter implements a sandbox that imposes strict
controls on what certain kinds of Java programs can and cannot do. To the extent that the sandbox
works, it allows a user to run untrusted code safely. As we discussed in Chapter 1, "Mobile Code
and Security: Why Java Security Is Important," an alternative approach to handling mobile code is
to run only code that is trusted. ActiveX controls, for example, are omnipotent and should be run
only when you completely trust the entity (person or corporation) that signed the control. It is clear
that future mobile code systems will involve both a sandbox model and a trust model based on code
signing. In fact, the two will likely be so intertwined that they cannot be easily teased apart. This is
already happening in Java.

There are many ways to impose a sandbox model on executable content-Java presents just one.
Since the Java sandbox model has been widely distributed to millions of users with their Web
browsers, it is safe to say that the Java sandbox is today's most widely used sandbox model. Until
recently, it was correct to assume that a Java sandbox placed particular constraints on Java applets.
JDK 1.1 changed all that; and as we pointed out in Chapter 1, the notion of a sandbox is becoming
ever more complex.

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 complicates things
immensely. As it now stands, the Java sandbox we detail in this chapter has been reduced to a
default. Chapter 3, "Beyond the Sandbox: Signed Code and Java 2," discusses how things work
when this default sandbox policy is not used.

Chapter Two Sections


1. Potential Threats
2. What Untrusted Java Code Can't Do
3. What Untrusted Java Code Can Do
4. The Java Language and Security
5. The Three Parts of the Default Sandbox
6. The Verifier
7. The Class Loader Architecture
8. The Security Manager
9. Different Classes of Security
10. Type Safety
11. Browser-Specific Security Rules
12. The Fundamental Tradeoff
13. Is There Really a Java Security Policy?

The Base Java Security Model: The Original Applet


Sandbox
CHAPTER SECTION:1

Section 1 -- Potential Threats


Java applets are far more powerful than the usual HTML code served up on the Web. When not
restricted by applet-security measures, Java is a complete and powerful programming language
capable of sending information over the network; reading, altering, or deleting files; using system
resources; and so on. This is powerful stuff, and in the hands of a malicious programmer (or even
just a sloppy or incompetent programmer), Java code could do some damage to a user's system.
People surfing the Web should not be burdened with such worries (although developers and system
administrators unfortunately must be). So from a user's perspective, Java should put these worries
to rest by providing an automatic security solution. Java should restrict itself such that the full
power and potential of the Java language is not misused. After all, who wants to run a Java applet
that erases your hard disk? And who wants to develop or distribute an applet that accidentally
erases someone else's hard disk?

The design problem lies in the fact that programs running on a personal computer usually have
unlimited access to all of the machine's resources. (That's what we mean when we say that ActiveX
controls are omnipotent.) Most PC applications are total system tyrants while they run. But if the
Java applets you retrieve from the Web have been written by someone else, you should not trust
them to perform with integrity. Unless you grant Java code special privileges, Java downloaded
from the Net is automatically considered untrusted code. In order to ensure that untrusted code does
nothing mischievous, it is important to limit what that untrusted code can do. Of course, completely
limiting access to a system defeats the purpose of having executable content in the first place. After
all, who wants to run a program that is not allowed to do anything? And who wants to develop or
distribute such a program?
Somehow these two extremes need to be balanced. Java applets need enough power to do some
things and sufficient restrictions so that they can't do others. The solution is controlling access to
system resources carefully. This is what the Java security model aims to do.

Before we talk about the internals of the Java security model, it is important to discuss the potential
problems raised by mobile code. There are four basic categories of potential attacks Java applets
could facilitate:

• Attacks that modify the system


• Attacks that invade a user's privacy
• Attacks that deny legitimate use of the machine by hogging resources
• Attacks that antagonize a user

Table 2.1 lists the four classes in order of severity. There will be a brief discussion of each in turn.
Keep in mind that this list of attacks is meant only to give a flavor of the kinds of things possible; it
is by no means a complete list. Also note that the four attack classes overlap. As we shall see,
hostile applets often fall into more than one category.
ATTACK JAVA
EXPLANATION AND CONSEQUENCES
CLASS DEFENSE

The most severe class of attacks. Applets that implement


System
such attacks are attack applets. Consequences of these Strong
Modification
attacks: severe.

If you value your privacy, this attack class may be


Invasion of particularly odious. They are implemented by malicious
Strong
Privacy applets. Include mail forging. Consequences of these
attacks: moderate.

Also serious but not severely so, these attacks can bring
Denial of a machine to a standstill. Also implemented by
Weak
Service malicious applets. May require reboot. Consequences of
these attacks: moderate.

Merely annoying, this attack class is the most commonly


encountered. Implemented by malicious applets. May
Antagonism Weak
require restart of browser. Consequences of these
attacks: light to moderate.
Figure 2.1

The relative severity of the four attack classes varies depending on the situation. For a lone user
browsing on a personal computer, system modification attacks and privacy attacks are serious, but
the other two classes are only a mild inconvenience. For big enterprise servers, however, attacks
that antagonize employees and customers or deny them service altogether are serious issues.

System Modification
Java is a very powerful programming language, and with this power comes the potential for abuse.
Most programming languages give programs the ability to read and modify data on the system
where they are running. Java includes predefined classes with methods that can delete and
otherwise modify files, modify memory, and even kill processes and threads. System modification
attacks comprise the most critical risks. Java's designers have given much thought to preventing
this class of attack.

In the most serious cases, system modification involves intrusion into the system itself. Like many
parts of today's complex systems, Java can be misused as an avenue of attack. Given that crackers
will use any tool available to compromise the security of a machine, special care must be taken to
ensure that Java does not provide new ports of entry to a machine. That Java is designed to work on
many different platforms makes this task that much more important.1

The good news is that using Java to break into a machine is not easy. The bad news is that such
break-ins are certainly possible. This book refers to applets that implement system modification
attacks as attack applets. Attack applets are a serious concern. In Chapter 5, "Attack Applets:
Exploiting Holes in the Security Model," you will see how several such attacks have been
successfully discovered and applied in the laboratory. Patches have been developed that make these
attacks impossible, but the threat of other sophisticated attacks remains.

A good example of a system modification attack involves a security hole discovered in JDK 1.1.1
by the Princeton team in February 1997. The hole has since been fixed. The particular hole was a
problem in the way code signing was implemented. As a result of a simple error in the JDK 1.1.1
code signing system (returning a mutable array on a Class.getsigners() method call instead of a
copy of the array), an attack applet signed with a fake signature could escape the sandbox
completely and acquire all the privileges available to completely trusted code. Once outside the
sandbox, the attack applet could do anything at all, including installing a virus or a Trojan Horse.
Put in simple terms, any machine attacked through the code signing hole could be completely
compromised. More detail on the code signing hole and an attack applet called the Magic Coat is
found in Chapter 5.

With so many machines running mission-critical applications, system modification attacks could
lead to problems such as modified financial records in a database, which could in turn lead to
financial loss and corporate bankruptcy. Modified medical records could result in fatally incorrect
treatment. Care must be taken not to expose critical systems to new lines of attack. Crackers flock
to the latest and greatest vulnerabilities. It is important that Java not become a cracking vehicle.

It is important to emphasize that no system modification attacks using Java have been seen outside
the lab. That's lucky because if one of these attacks were invented by crackers, Java viruses could
become a reality. Attack applets designed to carry out system modification attacks are the subject
of Chapter 5.

Invasion of Privacy
A second general type of attack involves disclosing information about a user or host machine that
should not be publicized. Some files are meant to be kept confidential. For example, on Unix
machines, if someone gains access to the /etc/passwd file (which contains usernames and encrypted
passwords) he or she could mount a password-cracking attack. A successful password-cracking
attack is a complete invasion of a machine and can be escalated into a system-modification attack.

Sensitive information of other sorts can also be leaked from a system. Consider the implications of
an unscrupulous company being able to steal the secret business plans of a competitor through
corporate espionage. Or, if you are not a corporate user, consider your private email
correspondence or your financial records being made public. If such confidential information is
mailed or otherwise transferred off a system, the act can be called an invasion of privacy.

A particularly egregious risk, especially in today's e-commerce systems based on the use of
cryptography, is loss of a private key. Consider that in Utah, legislation can be enacted into law
through the application of a digital signature. If the governor's private key falls into the wrong
hands, legal ramifications would be interesting at best (and disastrous at worst). Private keys should
be among the most closely guarded secrets in existence.

Most modern workstations include sound capability. This opens up users to a new kind of
eavesdropping. If an attacker can gain control of the microphone, then it is possible to listen in on
the area immediately surrounding the workstation. More subtle eavesdropping includes monitoring
process tables and file access. A Web-based version of eavesdropping might include keeping track
of which links a user follows.

Forging mail could also be construed as a kind of invasion of privacy attack. If an outsider can gain
enough information to forge mail that appears to be from you, then you are exposed to a large
number of serious risks. As is discussed in Chapter 4, "Malicious Applets: Avoiding a Common
Nuisance," Java makes the standard mail-forging attack a much more serious threat. Java
successfully defends against some of these attacks. For example, file I/O is very closely guarded.
However, this good feature is countered by the fact that applets always have a channel open back to
their original host server. Should the applet somehow dig up some information, it would be very
easy to send that information back to the original host server. Non-file-related members of the
invasion of privacy class (such as mail forging) are harder to defend against. Short of defenses such
as disabling a system's network ports, the mail forging attack is likely to remain a threat.

Denial of Service

Denial of service attacks make system resources unavailable. They occur, for example, when a
process eats up more than the standard allotment of resources, essentially hogging the machine.
There are many subcategories of denial of service attacks. Some examples include:

• Completely filling a file system


• Using up all available file pointers
• Allocating all of a system's memory
• Creating thousands of windows, effectively denying access to the output screen or window
event queue
• Using all of the machine's cycles (CPU time) by creating many high-priority threads

There are also some types of denial of service attacks that do not hog the systems resources, but
lock up the system in some other way; for example, by acquiring a lock on some critical system
resource, or by causing the system to wait for an input or output operation that can never complete.
Although denial of service attacks are a real concern, Java's designers were not able to protect users
from this class of attacks.

There is some debate over the relative importance of stopping denial of service attacks. In most
cases, denial of service is more closely related to the class of annoyance attacks than to anything
else. This is because recovering from a denial of service attack is usually not difficult (simply
reboot the machine). But some computer systems perform very important, even mission-critical
tasks. Denial of service to such a machine could be very serious. Consider the mayhem that would
erupt should a malicious program lock up the machines running the stock market. Losses could be
staggering.

Denial of service attacks are by far the most commonly encountered Java security concern.
Implementing such an attack is not hard, but stopping one is. Malicious applets, the subject of
Chapter 4, often make use of denial of service attacks. Unfortunately, the current security model
does not offer a good solution to the denial of service problem. Planned enhancements to the Java
security model promise to lessen the threat posed by denial of service attacks. Security experts
know, however, that denial of service attacks are very tough to prevent, so we expect Java will not
have strong defenses against denial of service any time soon.

If you are running a Java-enabled server application, you should be worried about denial of service
attacks. If your server is configured to allow untrusted clients to upload "servlets" into your server,
then any Internet user, anywhere in the world, can upload a denial of service servlet and crash your
server. Your best defense is to make sure your server is configured to accept servlets only if they
are written by people you trust. Remember, the Java sandbox does not protect you against denial of
service attacks. Figure 2.1 shows how servlets differ from applets.

Figure 2.1 Servlets in Java are the server-side equivalent of


applets for the client.

The idea is to provide a VM inside a Web server on which servlets can


be run to carry out tasks once delegated to CGI scripts. When mobile
code runs on a Web server, the risk of denial-of-service attack carries
more importance. Servlets often access back-end databases on behalf
of a Web server.

Antagonism

Less odious, but still of some concern, are attacks that merely antagonize or annoy a user. Playing
unwanted sound files through a speaker or displaying obscene pictures on a monitor are two
examples. Sometimes seemingly antagonistic attacks may be the result of simple programming
errors. Chapter 4 contains some examples of antagonistic malicious applets.

Some denial of service attacks could be classified as merely antagonistic. A denial of service attack
that opens many windows simultaneously, for example, can be reduced to an annoyance depending
on the window manager in use. Many window managers require user intervention to place a
window and, thus, open only one window at a time. Judging the severity and category of a
particular attack is always a subjective and context-sensitive problem. This is no reason to pretend
that such categories do not exist. In order to more thoroughly understand the risks associated with
mobile code, these attacks need to be considered.

Java Risks in Perspective

It is important to emphasize that using Java is not the only way to carry out the four classes of
attack discussed previously (system modification, invasion of privacy, denial of service, and
antagonism). In fact, the risk of being hit with a non-Java-based system modification attack is much
greater than the risk of an attack applet exploiting a hole in the Java security model to modify your
system. There are countless non-Java examples of all four of attack classes sprinkled throughout
the computer security literature. If you are interested in these topics, one of the most encyclopedic
sources of information is the book Computer Related Risks by Peter G. Neumann [Neumann,
1995]. In any case, the book you're reading now is about Java security, so we will emphasize Java's
vulnerability to attacks and protections against these attacks.

Java's designers are primarily concerned with stopping the worst potential attacks that hostile
applets might carry out; that is, system modification and invasion of privacy attacks. One kind of
mobile code that everyone wants to avoid is a computer virus. Though viruses traditionally target
personal computers and have yet to make much of a dent in the networked Unix world, Java might
well have changed all that. Fortunately, Java's security model seems to have been successful so far
in thwarting the possibility of cross-platform viruses. Simply put, there have been no Java applet
viruses (yet).2

That is not to say, however, that a Java virus is impossible. Despite claims to the contrary, any hole
in the security sandbox would allow a Java virus to propagate. A system modification attack on a
user's browser could choose to modify that user's homepage (and any other Web pages it could
find), thereby propagating itself virus-like onto more Web pages.
The remaining attacks, denial of service and antagonism, are much harder to stop. Denial of service
can be as simple as using all system resources, and it is sometimes hard to decide what constitutes a
legitimate but taxing use of, say, your CPU, and what is an attack. The same thing goes for
annoyance. A thread that never dies in one applet may be useful (if the applet is doing something
like perpetually updating a stock quote in a window), while a thread that never dies in a hostile
applet may be doing something evil such as monitoring your Web usage.

For a number of reasons, the market appears to consider Java security risks important. It all started
when Sun included "secure" in the Java buzzwords list. The research community took this as a
challenge, and the holes described in this book began to be discovered. These days, perceived Java
security risks may, in fact, outstrip actual risks, though any such judgment needs to be made on a
case-by-case basis.

One side effect of the large amount of attention paid to Java security is the proliferation of Java
security vendors who market products that attempt to block all Java applets, scan applets for what
might be hostile behavior before they run, or banish Java applet code to a central "sacrificial"
server. If you are considering any of the many Java security enhancement products on the market,
one good exercise is to ask how successful such products are in practice against each of the four
attack classes introduced here. We will revisit this topic in Chapter 6, "Securing Java:
Improvements,Solutions, and Snake Oil."

CHAPTER SECTION:2

Section 2 -- What Untrusted Java Code Can't Do


Trusted Java applications do not have the same security implications as untrusted applications
(including a majority of applets found on the Web). Thus, with a trusted application, it is possible
to read and write files, communicate with devices, connect to sockets, and so on. But untrusted
code is different. Clearly, untrusted code needs to be stopped from doing some of these things.
Untrusted access to the more powerful features of Java must be properly restricted.

From the perspectives of both end users and system administrators, the most important Java
security issues crop up when we talk about applets. Applets are Java's most pervasive version of
mobile code (servlets have a bit of catching up to do). There are many things that untrusted Java
applets should not be allowed to do and many resources to which Java applets should have only
restricted access. The Java sandbox imposes these restrictions. With the introduction of Java 2, it is
possible to treat applications as untrusted as well-running them inside the sandbox.

Applets can be related to the traditional client/server model in a straightforward manner: The Web
server is the applet's server. It sends the applet to the client machine. The client is the machine on
which the applet eventually runs. That means when you are surfing the Web and come across an
applet, your machine is the client. This terminology is useful for explaining what applets are not
allowed to do.
Note that the following list of no-no's defines only one commonly encountered kind of Java
sandbox. Up until the advent of JDK 1.1, all Web-based applets were required to adhere to these
rules (although enforcement was up to the browser being used). These days, sandboxes come in
many assorted shapes and sizes. Untrusted applets that have not been granted special privileges are
relegated to the default sandbox of the old days. Partially trusted applets can be given more leeway
(or put another way, they can be given a larger sandbox to play in). As we shall see in Chapter 3,
the size and shape of a Java sandbox is now at a Java user's complete discretion. Though we present
the following list of constraints in terms of untrusted applets, untrusted applications can be run
within the default sandbox as well. The sandbox, which is the base Java security model,
implements the default constraints. If an untrusted applet has been loaded across the network and
the default security policies are intact, the applet is not allowed to:

• Read files on the client file system.


• Write files to the client file system.
• Delete files on the client file system, either by using the File.delete() method, or by calling
system-level rm or del commands.
• Rename files on the client file system, either by using the File.renameTo() method, or by
calling system-level mv or rename commands.
• Create a directory on the client file system, either by using the File.mkdirs() methods or by
calling the system-level mkdir command.
• List the contents of a directory.
• Check to see whether a file exists.
• Obtain information about a file, including size, type, and modification timestamp.
• Create a network connection to any computer other than the host from which it originated.
• Listen for or accept network connections on any port on the client system.
• Create a top-level window without an untrusted window banner.
• Obtain the user's username or home directory name through any means, including trying to
read the system properties: user.name, user.home, user.dir, java.home, and java.class.path.
• Define any system properties.
• Run any program on the client system using the Runtime.exec() methods.
• Make the Java interpreter exit, using either System.exit() or Runtime.exit().
• Load dynamic libraries on the client system using the load() or loadLibrary() methods of the
Runtime or System classes.
• Create or manipulate any thread that is not part of the same ThreadGroup as the applet.
• Create a ClassLoader.
• Create a SecurityManager.
• Specify any network control functions, including ContentHandlerFactory,
SocketImplFactory, or URLStreamHandlerFactory.
• Define classes that are part of packages on the client system.

Java applets are typically executed by Web browsers with embedded Java Virtual Machines (VMs)
and runtime class libraries. Applets are downloaded by the browser and then executed by the built-
in VM on the machine running the browser. The security of the system depends on these parts of
the model: the Java language itself, the runtime class libraries, and the Security Manager of the
browser. The next section examines how the Java compiler and runtime system restrict the creation
and distribution of malicious code.

CHAPTER SECTION:3

Section 3 -- What Untrusted Java Code Can Do


An alternative approach to presenting the sandbox is to define what untrusted applets can do. After
all, if applets were allowed to do nothing, security concerns would be completely alleviated. In
order to run, untrusted applets require access to the CPU of the client machine as well as access to
memory in which to build objects. Access to both of these resources is completely controlled by the
VM. The default sandbox for untrusted applets also includes access to the Web server from which
the applet was downloaded. Programmers not used to such constraints often complain that the
default sandbox is too restrictive; for example, the inability to read and write temporary files must
be designed around.

The good news (for developers, anyway) is that Java 2 allows the user to give a digitally signed
applet access to more local resources. The bad news (for users and administrators) is that in terms
of the usual tradeoff between functionality and security, more powerful applets present more risk.
In any case, applet developers are likely to code their applets to make use of local files, peripherals,
and networked resources. Then the applets will be digitally signed. In order for a signed applet to
run on a client machine, the administrator of the machine must set up a security policy that grants
the particular applet access to the resources it needs. Setting up and administering these policies is
not a trivial exercise. See Chapter 3 for more on these issues.

CHAPTER SECTION:4

Section 4 -- The Java Language and Security


Many of the features of Java as a language (as introduced in Chapter 1) have important security
implications. This is important for everyone: for developers charged with creating secure code; for
users who must in the end rely on developers' products; for system administrators who install,
configure, and manage the products; and for managers who decide which products are developed
and how they are deployed. It is a well-known fact in computer security that security problems are
very often a direct result of software bugs. That leads security researchers, including the authors, to
pay lots of attention to software engineering. The hope is to avoid the ever-present penetrate-and-
patch approach to security by developing more secure code in the first place. Although Java is no
silver bullet, it can certainly help.

Object-orientation and a modern memory model both turn out to have a positive impact on Java
security. The notions of data hiding, abstraction, and encapsulation, in particular, can help
encourage better design for security. In Java, objects cannot be directly manipulated by a
programmer; instead, they can only be accessed through their public interfaces. This is a good
thing. Programmers cannot directly access memory either, but must use object references. This fact
makes it much harder for a nasty program to trawl through memory looking for interesting data
such as passwords and credit card numbers.

All of these features also help to make Java programs a bit more robust (on average) than programs
written in other popular languages. After all, mechanisms that prevent programs from deliberately
accessing memory that should be off-limits will also prevent buggy programs from accidentally
accessing the wrong memory. Since most security flaws exist because of programming bugs,
anything that reduces the general level of bugginess in software is good for security. Java helps
conscientious programmers reduce the odds that their code contains security bugs.

Every object and primitive data element in Java has a built-in access level associated with it, either
the explicit private, protected, or public access level, or a default access level.

• Private variables and classes can only be accessed by the class that created them.
• Protected variables and classes can only be accessed by the class that created them, the
creator's subclasses, and classes in the same Java package.
• Public variables and classes are accessible by all classes.
• By default, entities in a class can be accessed by code within the class that defines them, or
by a class in the same package as the class that defines them.

The access levels should be mostly familiar to C++ developers, although the Java definitions differ
slightly. (Access levels star in one of Java's security holes, as we will see in Chapter 5.)

There is an essential difference between languages like C++ and Java that stems directly from
Java's requirement that objects can only be manipulated through standard interfaces. In C++, a
programmer could declare certain variables inside an object private, but then access these variables
in sneaky ways. Though the C++ compiler may stop obvious front-door operations (for example,
not allowing an arbitrary class to access private elements of another), the back door is wide open.
Using pointer arithmetic, casting, and other such means, a C++ programmer can scan through a
program's allocated memory (and in fact, manipulate memory) at will. Why anyone would
deliberately do such a thing inside his or her own code is an interesting question (although it might
be done accidentally), but this sort of thing matters in Java where distinct applets that don't trust
each other may share the same VM. The access levels described here help address this concern.
Also helping to safeguard memory in Java are the following:

• Objects and methods declared final cannot be changed or overridden (ignoring this rule
would seriously break the Security Manager, among other things).
• Array bounds are checked for all array accesses (putting to rest a very common mistake that
plagues C).
• Object casting is restricted (necessary to ensure type safety).
• Variables cannot be used before they are initialized (another memory-protection
mechanism).
• Garbage collection automatically frees memory when it is no longer needed.
Type safety is certainly a key language feature in Java. It is important enough to Java security that
it deserves its own section (see page 74). For now, suffice it to say that making sure the VM does
not become confused about the kinds of objects it is using is essential. This is pretty obvious if you
think about the fact that the security model itself is a collection of classes with certain types.

In this section, we have covered a nice set of features of Java that can help make it easier to write
secure code (and help you rest easier if you use such code). However, how are these rules
enforced? That is up to the Java sandbox.

CHAPTER SECTION:5

Section 5 -- The Three Parts of the Default Sandbox


In addition to many safety-related characteristics of the Java language, Java security relies on a
multipart defense. The default sandbox is made of three interrelated parts: the Verifier, the Class
Loader, and the Security Manager. In the early days (way back in 1996), people often referred to
the Java security model as a "three-layer" defense. Though such a label is still encountered at times
in the Java security literature, it is misleading. The layer terminology implies that if an applet
penetrates the first "layer," two layers are left to set things straight. Actually, the parts are more like
links in a chain: If any of the three parts breaks, the entire security system breaks. Figure 2.2
emphasizes that for the security model to work, each of its parts must work perfectly.

Figure 2.2 The original sandbox model has three parts.

The three parts are: the Verifier that helps ensure type safety, the Class
Loader that loads and unloads classes dynamically from the Java runtime
environment, and the Security Manager that acts as a security gatekeeper
guarding potentially dangerous functionality. This figure emphasizes that
each of the three parts must do its job properly for the sandbox to work. If
any of the three parts fails, the security model is completely compromised,
leaving the door wide open for attack.

With the introduction of JDK 1.1 and Java 2, the Java security model was extended to include more
parts. First came the java.security package. This package is an API that includes both a
cryptographic provider interface (by which different cryptography implementations can be added
into Java) and APIs for common cryptographic algorithms. (These are sometimes called the
"security" classes, though this is a misnomer. Security consists of much more than just
cryptography.) It is this functionality that allows the VM to authenticate signed Java classes.
Another part, the Access Controller, was introduced with JDK 1.1 and significantly enhanced with
Java 2. We learn more about the security package and the Access Controller in Chapter 3.

The next three sections are each devoted to one part of the base security model: the Verifier, the
Class Loader, and the Security Manager. Each of these parts depends in some way on the others.
For the security model to function properly, each part must do its job flawlessly, and the parts must
fit together perfectly. Between them, the three parts perform load time and runtime checks in order
to restrict file system and network access (as well as restricting access to browser internals).

CHAPTER SECTION:6

Section 6 -- The Verifier


Recall that when a Java program is compiled, it compiles down to platform-independent Java byte
code. As Figure 2.3 shows, Java byte code is verified before it can run. This verification scheme is
meant to ensure that the byte code, which may or may not have been created by a Java compiler,
plays by the rules. After all, byte code could well have been created by a "hostile compiler'' that
assembled byte code meant to violate the rules of the Java VM; created directly with an editor like
emacs; created almost directly with a Java byte code assembler like Jasmin; or compiled from
another source language like C++, Scheme, or Ada into Java byte code. The important thing is not
what the source code looked like or even what language it was written in, but what the byte code
(the code that actually ends up running) does. In this sense, the Verifier makes mystery code a bit
less mysterious.

Figure 2.3 The Verifier scrutinizes byte code before it is allowed


to be run on a local VM.

The Verifier plays an essential role in Java's language-based


approach to security, which is built on the foundation of type safety.

Verifying class files containing byte code is one way in which Java automatically checks untrusted
code before it is allowed to run. Once Java code has been verified, it can execute in uninterrupted
fashion on a VM (with much less need to make security-critical checks while the code runs). This
strategy leads to improvements in the efficiency of Java execution, which offset the speed concerns
raised by Java's security checking. The Verifier is built in to the VM and cannot be accessed by
Java programmers or Java users. In most Java implementations, when Java code arrives at the VM
and is formed into a Class by the Class Loader, the Verifier automatically examines it. The Verifier
checks byte code at a number of different levels. The simplest test makes sure that the format of a
code fragment is correct. On a less-basic level, a built-in theorem prover is applied to each code
fragment. The theorem prover helps to make sure that byte code does not forge pointers, violate
access restrictions, or access objects using incorrect type information. If the Verifier discovers a
problem with a class file, it throws an exception, loading ceases, and the class file never executes.
The verification process, in concert with the security features built into the language and checked at
runtime, helps to establish a base set of security guarantees.3

The Verifier also ensures that class files that refer to each other preserve binary compatibility.
Because of Java's ability to dynamically load classes, there is the possibility that a class file being
dynamically linked may not be compatible with a referring class. Binary incompatibility problems
could occur when a library of Java classes is updated or when a class from a large Java program is
not recompiled during development. There are rules of compatibility that govern the ability to
change use of classes and methods without breaking binary compatibility [Venners, 1998]. For
example, it is okay to add a method to a class that is used by other classes, but not okay to delete
methods from a class used by other classes. Compatibility rules are enforced by the Verifier.

Binary incompatibility also has security implications. If a malicious programmer can get your VM
to accept a set of mutually incompatible classes, the hostile code will probably be able to break out
of the sandbox. This problem happened several times with early VM implementations. (See, for
example, You're Not My Type, in Chapter 5.)

Java users and business people investigating the use of Java in their commercial enterprises often
complain about the length of time it takes for a Java applet to get started running in a browser. E-
commerce system designers paint startup delay as a business-side show stopper, citing the fact that
consumers do not react well even to a 20-second delay in their shopping experience. Many people
believe falsely that the main delay in starting applets is the time it takes to download the applet
itself. But given a reasonably fast connection to the Internet, what takes the longest is not
downloading the code, but verifying it. The inherent costs of verification fit the classic tradeoff
between functionality and security to a tee. As security researchers, we believe the security that
byte code verification provides is well worth the slight delay. We also think it is possible to speed
up the verification process so that its execution time is acceptable.

In order to work, the Verifier reconstructs type state information by looking through the byte code.
The types of all parameters of all byte code instructions must be checked, since the byte code may
have come from an untrustworthy source. Because of this possibility, the Verifier provides a first
line of defense against external code that may try to break the VM. Only code that passes the
Verifier's tests will be run.

The process of Verification in Java is defined to allow different implementations of the Java VM a
fair amount of flexibility. The VM specification lists what must be checked and what exceptions
and errors may result from failing a check, but it does not specify exactly when and how to verify
types. Nevertheless, most Java implementations (especially the most widely used commercial
VMs) take a similar approach to verification. The process is broken into two major steps: internal
checks that check everything that can be checked by looking only at the class file itself and runtime
checks that confirm the existence and compatibility of symbolically referenced classes, fields, and
methods.

Through the two kinds of checks, the Verifier assures a number of important properties. Once byte
code passes through verification, the following things are guaranteed:

The class file has the correct format, including the magic number (0xCAFEBABE) and
proper length. (Much of this trivial checking can take place as a class file loads.)

Stacks will not be overflowed or underflowed. Overflowing stacks is a common attack on


programs written in other languages such as C that has led to several of the most notorious
security vulnerabilities. For example, the Internet worm used stack overflow as part of its
arsenal [Spafford, 1989]. Java assigns each thread two stacks: a data stack and an operand
stack. The data stack is the kind of stack C programmers are all too familiar with. It includes a
series of frames that hold local variables and provide some storage needed for method
invocation. The Verifier cannot prevent overflow of the data stack, and a denial of service
attack that takes advantage of this fact can be trivially implemented with a recursive function.
(A demonstration applet that carries out this attack can be found
at www.cs.nps.navy.mil/research/languages/DynApplet.html.) The operand stack (which is
itself allocated on the data stack) holds the values that each method invocation in Java byte
code operates on. This is the stack that the Verifier tracks and evaluates.

Byte code instructions all have parameters of the correct type. For example, integers are
always used as integers and nothing else. Method descriptors, which include both a return type
and the number and types of parameters, are checked with a context-free grammar [Venners,
1998].

No illegal data conversions (casts) occur. For example, treating an integer as a pointer is not
allowed. Correctly handling all potential casting combinations is a tricky undertaking. The
rules are complex, making a perfect implementation nontrivial. Although many checks are
done by the Verifier, some are deferred until runtime.

Private, public, protected, and default accesses are legal. In other words, no improper
access to restricted classes, interfaces, variables, and methods will be allowed.

All register accesses and stores are valid. Computer scientists refer to the meaning of a
language as its semantics, and the structure of a language is its syntax. When you are thinking
about what a language can do, it is useful to talk about semantics. The semantics of the Java
language provide much of Java's built-in security. It is critical that the semantics of Java be
enforced in each and every Java program. Byte code is designed to contain enough symbolic
information that safety verification (double-checking the compiler-enforced safety rules) can
occur. Byte code specifies the methods of a class as a set of Java Virtual Machine instructions.
These instructions must pass a battery of tests before they can be run.

Class Files and Byte Code

When Java source code is compiled, the results of the compilation are put into Java class files,
whose names typically end with the .class or .cls extension. Java class files are made up of streams
of 8-bit bytes. Larger values requiring 16 or 32 bits are composed of multiple 8-bit bytes. Class
files contain several pieces of information in a particular format. Included in a class file are:

• The magic constant (0xCAFEBABE)


• Major and minor version information
• The constant pool (a heterogeneous array composed of five primitive types)
• Information about the class (name, superclass, etc.)
• Information about interfaces
• Information about the fields and methods in the class
• Debugging information

[Sun Microsystems, 1996b; Sun Microsystems, 1996c] Much more information on class file
formats and byte code syntax can be found in [Venners, 1998].

The byte code inside a class file is made up of instructions that can be divided into several
categories. Among other things, byte code instructions, called opcodes, implement:

• Pushing constants onto the stack


• Accessing and modifying the value of a VM register
• Accessing arrays
• Manipulating the stack
• Arithmetic instructions
• Logic instructions
• Conversion instructions
• Control transfer
• Function return
• Manipulating object fields
• Invoking methods
• Creating objects
• Type casting

Since it exists at the level of the VM, Java byte code is very similar to assembly language. Each
line of byte code is a one-byte opcode followed by zero or more bytes of operand information. All
instructions (with the exception of two table lookup instructions) are of fixed length.

Opcodes and their associated operands represent the fundamental operations of the VM. Every
method invocation in Java gets its own stack frame to use as local storage for variable values and
intermediate results. As discussed earlier, the intermediate storage area part of a frame is called the
operand stack. Opcodes refer to data stored either on the operand stack or in the local variables of a
method's frame. The VM uses these values as well as direct operand values from an instruction as
execution data.

Internal Checks
Considered one level above the nitty-gritty level, class verification (of which byte code verification
is a crucial step) is usually said to occur in four passes. The first three passes implement the
Verifier's internal checks. The last pass implements the runtime checks. (Why runtime checking is
considered a part of the load-time verification process is a mystery to us, but Sun seems to have
convinced everyone to call it a fourth verification pass, so we'll go along.) The passes are:

1. Ensure that the class file is in the proper format. This includes checking the magic number
and making sure that all attributes are of the right length. The byte code cannot be too short
or too long, and the constant pool is able to be parsed.
2. Verify anything that can be done without looking at the opcodes. This includes the following
checks:
1. final classes cannot be subclassed, and final methods cannot be overridden.
2. Every class must have a superclass (except the class java.lang.Object).
3. The constant pool must satisfy more stringent constraints.
4. All field references and method references in the constant pool must have legal
names, legal classes, and a legal type signature.
3. Verify the byte code using data-flow analysis. At any given point in the byte code program,
no matter how that point is reached, all of the following must hold:
1. The operand stack is always the same size and includes the same types.
2. Register access is checked for proper value type.
3. Methods are called with the appropriate number and types of arguments.
4. Fields are modified with values of the appropriate type.
5. All opcodes have proper type arguments on the stack and in the registers.
6. Variables are properly initialized. (See [Sun Microsystems, 1996c].)
4. Perform at runtime any checks that were not done at verification time. Some of these checks
might have been impossible at verification time since some aspects of Java's type system
cannot be statically checked, and some checks might have been deferred to runtime for
implementation convenience.

Step 3, in which the actual byte code is verified, is a complex process that is carried out in two
passes by Sun's Verifier. (Other vendors' Verifiers may behave differently.) The first pass identifies
individual opcodes and stores them in a table. Once all opcodes are identified, the second pass
parses each opcode's operands. During the second pass, a structure is built for each byte code
instruction. This structure is evaluated for syntactic correctness by checking that:

• Flow control related instructions branch to valid instructions.


• Local variable references are legal (associated with the proper method).
• Use of constant pool entries follows typing rules.
• Opcodes have the correct number of arguments.
• Exception handlers start and end with valid instructions, and the start point comes before the
end point.

These checks, along with data flow analysis that tracks behavior of the operand stack and local
variables, make up Step 3.

Runtime Checks and Dynamic Loading


In addition to the internal verification steps, some runtime checks occur during class execution. For
example, whenever an instruction calls a method, or modifies a field, the runtime checks ensure
that the method or field exists, check the call for the proper form, and check the executing method
for access privilege.

In practice, the internal checks performed by the Verifier occur very soon after the VM loads a
class file. Loading all possible classes that might be called in a particular execution of a class is not
the most efficient approach. Instead, Java loads each class only when it is actually needed at
runtime.

In order to verify a class, the VM must load in the definition of any not-yet-loaded class that is
referenced by the class being verified. More precisely, the class being verified refers to some other
classes by name, and the VM needs to decide exactly which class each name refers to, so it can
replace the reference-by-name with a reference to a specific class object. This process is known as
dynamic linking, and has proven to be a persistent source of security problems.

As in the Verifier's internal checking, dynamic linking can throw an error should it fail. An invalid
reference happens when a referenced class does not exist, or when a referenced class exists but
does not contain some referenced field or method. Once an error of this sort is thrown, the class file
doing the reference is no longer considered valid.

The main benefit that the Verifier provides is that it speeds up execution by removing much of the
checking that would otherwise have to occur at runtime. For example, there is no runtime check for
stack overflow since it has already been done by the Verifier.

Checking the Checker

The Verifier disallows many obvious approaches to byte code manipulation. Nonetheless, a number
of researchers have succeeded in creating byte code that should be illegal, but nevertheless passes
the Verifier. The Princeton Secure Internet Programming team was the first to sneak attacks
involving illegal byte code past the Verifiers. Mark LaDue, creator of the Hostile Applet Home
Page (www.rstcorp.com/hostile-applets), performs a number of interesting experiments in which he
creates byte code that does not play by the rules and yet passes verification. Other Java security
researchers, most notably the Kimera group at the University of Washington, have discovered
problems in commercial Verifiers. Their work places special emphasis on correct verification. We
have more to say about the Verifier and Java holes in later chapters.

The Java Runtime and the Verifier

The Verifier acts as the primary gatekeeper in the Java security model. It ensures that each piece of
byte code downloaded from the outside plays by the rules.4 That way, the Java VM can safely
execute byte code that may not have been created by a Java compiler. When the Verifier finds a
problem in a class, it rejects the malformed class and throws an exception. This is obviously a
much more reasonable behavior than running buggy or malicious code that crashes the VM.
In order for the Verifier to succeed in its role as gatekeeper, the Java runtime system must be
correctly implemented. Bugs in the runtime system will make byte code verification useless. Most
of the available Java implementations appear to be mostly correct (see Chapter 5), but behavior
may vary from one implementation to another.

It would be nice for Sun or other interested parties to create comprehensive validation and
verification test suites for the entire Java development environment and publish the results of
testing the various Java implementations. Existing test suites have neither been completely
developed nor verified by outside experts. From a security perspective, this certainly presents a
problem. It is especially critical to verify any third-party Java environment to ensure that it properly
implements the Java run time. Without a guarantee of bug-free run time, Java security falls to
pieces. Java users should think carefully about the Java run time product that they use.

We should note, though, that one cannot construct a test suite that will find all security problems.
Although testing technology is improving, it can never be perfect-not even in theory. All we can
hope for is that security testing technology will find most of the bugs in new code before it is
released.

CHAPTER SECTION:7

Section 7 -- The Class Loader Architecture


ne of the central tenets of Java is making code truly mobile. Every mobile code system requires the
ability to load code from outside a system into the system dynamically. In Java, code is loaded
(either from the disk or over the network) by a Class Loader. Java's class loader architecture is
complex, but it is a central security issue, so please bear with us as we explain it.

Recall that all Java objects belong to classes. Class loaders determine when and how classes can be
added to a running Java environment. Part of their job is to make sure that important parts of the
Java runtime environment are not replaced by impostor code. The fake Security Manager shown in
Figure 2.4 must be disallowed from loading into the Java environment and replacing the real
Security Manager. This is known as class spoofing.

Figure 2.4 Spoofing occurs when someone or something


pretends to be something it is not.

In this figure, an external class has arrived from the Internet and
declares itself to be the Security Manager (in order to replace the real
Security Manager). If external code were allowed to do this, Java's
security system would be trivial to break.
Class loaders perform two functions. First, when the VM needs to load the byte code for a
particular class, it asks a class loader to find the byte code. Each class loader can use its own
method for finding requested byte code files: It can load them from the local disk, fetch them across
the Net using any protocol, or it can just create the byte code on the spot. This flexibility is not a
security problem as long as the class loader is trusted by the party who wrote the code that is being
loaded. Second, class loaders define the namespaces seen by different classes and how those
namespaces relate to each other. Namespaces are a subtle and security-critical issue that we'll have
a lot more to say about later. Problems with namespace management have led to a number of
serious security holes.

It probably would have been better if Java's design had initially separated the two functions of class
loaders and provided lots of flexibility in finding byte code but not much flexibility in defining
namespaces. In a sense, this is what has come about as successive versions of Java have had
increasingly restrictive rules about how namespaces may be managed. Java's class loader
architecture was originally meant to be extensible, in the sense that new class loaders could be
added to a running system. It became clear early on, however, that malicious class loaders could
break Java's type system, and hence breach security. As a result, current Java implementations
prohibit untrusted code from making class loaders. This restriction may be relaxed in the future,
since there is some possibility that the Java 2 class loader specification is at last safe in the presence
of untrusted class loaders.

Varieties of Class Loaders

There are two basic varieties of class loaders: Primordial Class Loaders and Class Loader objects.
There is only one Primordial Class Loader, which is an essential part of each Java VM. It cannot be
overridden. The Primordial Class Loader is involved in bootstrapping the Java environment. Since
most VMs are written in C, it follows that the Primordial Class Loader is typically written in C.
This special class loader loads trusted classes, usually from the local disk. Figure 2.5 shows the
inheritance hierarchy of Class Loaders available in Java 2.

Figure 2.5 Class Loaders provide Java's dynamic loading


capability, which allows classes to arrive and depart from the
runtime environment.
Java 2 implements a hierarchy of Class Loaders. This figure, after
Gong [Gong, 1998], shows the inheritance hierarchy of Class
Loaders.

The Primordial Class Loader


The Primordial Class Loader uses the native operating system's file access capabilities to open and
read Java class files from the disk into byte arrays.

This provides Java with the ability to bootstrap itself and provide essential functions. The Java API
class files (stored by default in the classes.zip file) are usually the first files loaded by the VM. The
Primordial Class Loader also typically loads any classes a user has located in the CLASSPATH.
Classes loaded by the Primordial Class Loader are not subjected to the Verifier prior to execution.

Sometimes the Primordial Class Loader is referred to as the "internal" class loader or the "default"
class loader. Just to make things overly complicated, some people refer to classes loaded by the
Primordial Class Loader as having no class loader at all.

Class Loader Objects


The second basic variety of class loader is made up of Class Loader objects. Class Loader objects
load classes that are not needed to bootstrap the VM into a running Java environment. The VM
treats classes loaded through Class Loader objects as untrusted by default. Class Loaders are
objects just like any other Java object-they are written in Java, compiled into byte code, and loaded
by the VM (with the help of some other class loader). These Class Loaders give Java its dynamic
loading capabilities.

There are three distinct types of Class Loader objects defined by the JDK itself: Applet Class
Loaders, RMI Class Loaders, and Secure Class Loaders. From the standpoint of a Java user or a
system administrator, Applet Class Loaders are the most important variety. Java developers who
are interested in rolling their own Class Loaders will likely subclass or otherwise use the RMI Class
Loader and Secure Class Loader classes.

Applet Class Loaders are responsible for loading classes into a browser and are defined by the
vendor of each Java-enabled browser. Vendors generally implement similar Applet Class Loaders,
but they do not have to. Sometimes seemingly subtle differences can have important security
ramifications. For example, Netscape now tracks a class not by its name, but by a pointer to actual
code, making attacks that leverage Class Loading complications harder to carry out.

Applet Class Loaders help to prevent external code from spoofing important pieces of the Java API.
They do this by attempting to load a class using the Primordial Class Loader before fetching a class
across the network. If the class is not found by the Primordial Class Loader, the Applet Class
Loader typically loads it via HTTP using methods of the URL class. Code is fetched from the
CODEBASE specified in the <APPLET> tag. If a fetch across the Web fails, a ClassNotFound
exception is thrown.
It should be clear why external code must be prevented from spoofing the trusted classes of the
Java API. Consider that the essential parts of the Java security model (including the Applet Class
Loader class itself) are simply Java classes. If an untrusted class from afar were able to set up shop
as a replacement for a trusted class, the entire security model would be toast!

The RMI Class Loader and Secure Class Loader classes were introduced with JDK 1.1 and Java 2,
respectively. RMI Class Loaders are very similar to Applet Class Loaders in that they load classes
from a remote machine. They also give the Primordial Class Loader a chance to load a class before
fetching it across the Net. The main difference is that RMI Class Loaders can only load classes
from the URL specified by Java's rmi.server.codebase property. Similar in nature to RMI Class
Loaders, Secure Class Loaders allow classes to be loaded only from those directories specified in
Java's java.app.class.path property. Secure Class Loaders can only be used by classes found in the
java.security package and are extensively used by the Java 2 access control mechanisms.

Roll-Your-Own Class Loaders


Developers are often called upon to write their own class loaders. This is an inherently dangerous
undertaking since class loading is an essential part of the Java security model. Homegrown class
loaders can cause no end of security trouble. The right approach to take in writing a class loader is
to avoid changing the structure of namespaces, and to change only the methods that find the byte
code for a not-yet-loaded class. This will allow you to fetch classes in new ways, such as through a
firewall or proxy, or from a special local code library, without taking the risk inherent in namespace
management. You can do this by overriding only the loadClass methods.

Namespaces

In general, a running Java environment can have many Class Loaders active, each defining its own
namespace. Namespaces allow Java classes to see different views of the world depending on where
they originate (see Figure 2.6). Simply put, a namespace is a set of unique names of classes loaded
by a particular Class Loader and a binding of each name to a specific class object. Though some
people say that namespaces are disjoint and do not overlap, this is not true in general. There is
nothing to stop namespaces from overlapping.

Figure 2.6 Class Loaders have two distinct jobs (which


we believe would have been better off separated): (1)
fetching and instantiating byte code as classes, and (2)
managing name spaces.

This figure shows how Class Loaders typically divide


classes into distinct name spaces according to origin. It is
especially important to keep local classes distinct from
external classes. This figure implies that name spaces do
not overlap, which is not entirely accurate.

Most VM implementations have used different class loaders to load code from different origins.
This allowed these implementations to assign a single security policy to all code loaded by the
same class loader, and to make security decisions based on which class loader loaded the class that
is asking to perform a dangerous operation. With the addition of code signing in JDK 1.1, there are
now two characteristics for categorization of code: origin (usually represented as a URL) and signer
(the identity associated with the private key used to sign the file). Only the Class Loader that loaded
a piece of code knows for sure where the code was loaded from.

Applet Class Loaders, which are typically supplied by the browser vendor, load all applets and the
classes they reference, usually getting the classes from HTTP servers. When an applet loads across
the network, its Applet Class Loader receives the binary data and instantiates it as a new class.
Under normal operation, applets are forbidden to install a new Class Loader, so Applet Class
Loaders are the only game in town.

A trusted Java application (such as the Java interpreter built in to Netscape Navigator or Internet
Explorer) can, however, define its own class loaders. Sun Microsystems provides three template
class loader modules as part of the JDK (discussed earlier). If an untrusted applet could somehow
install a Class Loader, the applet would be free to define its own namespace. Prior to Java 2, this
would allow an attack applet to breach security (see Chapter 5).

If you are writing an application or built-in extension that defines its own Class Loader, you should
be very careful to follow the rules; otherwise, your Class Loader will almost certainly introduce a
security hole. It is unfortunate that in order to get the ability to use your own code-finding
mechanism, you must also take on responsibility for managing namespaces. One criticism often
raised against the Java security model is that because of the presence of objects like application-
definable class loaders, the security model is too distributed and lacks central control. Applet Class
Loaders install each applet in a separate namespace. This means that each applet sees its own
classes and all of the classes in the standard Java library API, but it doesn't see classes belonging to
other applets. Hiding applets from each other in this way has two advantages: It allows multiple
applets to define classes with the same name without ill effect, so applet writers don't have to worry
about name collisions. It also makes it harder, though not impossible, for applets to team up.

As an example, consider a class called laptop with no explicit package name (that is, laptop belongs
to the default package). Imagine that the laptop class is loaded by an Applet Class Loader
from www.rstcorp.com as you surf the Java Security Web Site. Then you surf over to java.sun.com
and load a different class named laptop (also in the default package). What we have here is two
different classes with the same name. How can the VM distinguish between them? The tagging of
classes according to which Class Loader loaded them provides the answer. Applets from different
CodeBases are loaded by different instances of the browser's Applet Class Loader class. (By the
way, distinct namespaces will be created even if the two sites use explicit package names that
happen to be the same.) Although the same class is involved in loading the two different classes
(i.e., the Applet Class Loader), two different instances of the Applet Class Loader class are
involved-one for each CodeBase.
Recall that the default object protection and encapsulation scheme covered earlier in this chapter
allows classes that are members of a package to access all other classes in the same package. That
means it is important for the VM to keep package membership straight. As a result, Class Loaders
have to keep track of packages as well as classes.

When a class is imported from the network, the Applet Class Loader places it into a namespace
labeled with information about its origin. Whenever one class tries to reference another, the Applet
Class Loader follows a particular order of search. The first place it looks for a class is in the set of
classes loaded by the Primordial Class Loader. If the Primordial Class Loader doesn't have a class
with the indicated name, the Applet Class Loader widens the search to include the namespace of
the class making the reference.

Because the Applet Class Loader searches for built-in classes first, it prevents imported classes
from pretending to be built-in classes (something known as "class name spoofing"). This policy
prevents such things as applets redefining file I/O classes to gain unrestricted access to the file
system. Clearly, the point is to protect fundamental primitives from outside corruption.

Since all applets from a particular source are put in the same namespace, they can reference each
other's methods. A source is defined as a particular directory on a particular Web server.

According to the Java specification, every Class Loader must keep an inventory of all the classes it
has previously loaded. When a class that has already been loaded is requested again, the class
loader must return the already loaded class.

Loading a Class

Class loading proceeds according to the following general algorithm:

• Determine whether the class has been loaded before. If so, return the previously loaded class.
• Consult the Primordial Class Loader to attempt to load the class from the CLASSPATH.
This prevents external classes from spoofing trusted Java classes.
• See whether the Class Loader is allowed to create the class being loaded. The Security
Manager makes this decision. If not, throw a security exception.
• Read the class file into an array of bytes. The way this happens differs according to
particular class loaders. Some class loaders may load classes from a local database. Others
may load classes across the network.
• Construct a Class object and its methods from the class file.
• Resolve classes immediately referenced by the class before it is used. These classes include
classes used by static initializers of the class and any classes that the class extends.
• Check the class file with the Verifier.

Summary
Each Java class begins as source code. This is then compiled into byte code and distributed to
machines anywhere on the Net. A Java-enabled browser automatically downloads a class when it
encounters the <APPLET> tag in an HTML document. The Verifier examines the byte code of a
class file to ensure that it follows Java's strict safety rules. The Java VM interprets byte code
declared safe by the Verifier. The Java specification allows classes to be unloaded when they are no
longer needed, but few current Java implementations unload classes.

Java's ability to dynamically load classes into a running Java environment is fraught with security
risks. The class-loading mechanisms mitigate these risks by providing separate namespaces set up
according to where mobile code originates. This capability ensures that essential Java classes
cannot be spoofed (replaced) by external, untrusted code. The Applet Class Loader in particular is a
key piece of the Java security model.

CHAPTER SECTION:8

Section 8 -- The Security Manager


The third part of the base Java security model is the Security Manager. This part of the security
model restricts the ways an applet uses visible interfaces (Java API calls). The Security Manager
implements a good portion of the entire security model and is the part of the security model most
often encountered (in terms of a SecurityException) by Java applet developers.

The job of the Security Manager is to keep track of who is allowed to do which dangerous
operations. A standard Security Manager will disallow most operations when they are requested by
untrusted code, and will allow trusted code to do whatever it wants. The old distinction between
applets and applications in JDK 1.0.2 used to directly affect which code the Security Manager
managed. Applets, being completely untrusted, were subject to the strict rules of the Security
Manager, while applications, being completely trusted, were not. This often led to confusion in the
past, especially among managers who believed that the way the Security Manager was set up
somehow made trusted Java applications more secure. The fact is, running trusted Java applications
is just as risky as running any other executable code written in any language. If the trusted code is
malicious or buggy, you could be in big trouble.

Although the browser vendors have designed very similar Security Managers for the most popular
Java-enabled browsers, there is no strict edict forcing them to do this. The fact that Security
Managers applied to untrusted applets generally enforce the same policies makes things easier to
understand and leads to a more uniform experience for both Java users and Java applet developers.
But Security Managers don't have to follow an all-or-nothing approach to controlling dangerous
resources. They could, for example, be written to give specialized access to particular classes. The
fact is, Security Managers can be as simple or as complicated as their authors decide to make them.
To the extent that applet security is a major concern to you (and it must be, or you would not be
reading this book), your choice of a browser should also be of great concern.
This discussion focuses on two central characteristics of Security Managers: how they work, and
how they are set up to restrict the activities of untrusted applets in most browsers. Before the
introduction of code signing with JDK 1.1, the security policy for untrusted applets was easy to
understand (although its enforcement was complicated), and the goal of a Security Manager was
thus straightforward. Now that applets come in myriad trust levels, talk of a single Security
Manager makes less sense. To be sure, the Security Manager's default rules do serve as the default
under there somewhere, but like the rest of the sandbox, they are only a default.

How the Security Manager Works

The Security Manager is a single Java object that performs runtime checks on dangerous methods.
Code in the Java library consults the Security Manager whenever a potentially dangerous operation
is attempted. The Security Manager can veto the operation by generating a SecurityException.
Decisions made by the Security Manager take into account the origin of the requesting class.
Obviously, built-in classes are usually given more privilege than classes loaded across the Net. The
Security Manager makes the final decision as to whether a particular operation is permitted or
rejected. See Figure 2.7.

Figure 2.7 The Security Manager keeps tabs on


potentially dangerous calls to the native operating
system (underlying the VM).

In this way, Java can encapsulate resources that could


otherwise be abused by mobile code.

For applet developers, the Security Manager is a behind-the-scenes player that need not be directly
invoked. (In any case, no self-respecting hostile applet would deliberately invoke a security check
on itself!) The Java API provides all calls necessary to interface to the operating system, thus
making isolation of all required security checks possible within the API. When a dangerous call is
made to the Java library, the library queries the Security Manager. These queries use a set of
methods that check access. For example, the Security Manager in most browsers contains the
methods checkWrite() and checkConnect(), which check whether to allow file writing and the
creation of network connections, respectively. If the check passes muster, the call quietly returns;
otherwise, a security exception is thrown.

The Java library's use of the Security Manager works as follows:

• A Java program makes a call to a potentially dangerous operation in the Java API.
• The Java API code asks the Security Manager whether the operation should be allowed.
• The Security Manager throws a SecurityException back to the Java API if the operation is
denied. This exception propagates back to the Java program.
• If the operation is permitted, the Security Manager call returns without throwing an
exception, and the Java API performs the requested dangerous operation and returns
normally.

Each VM can have only one Security Manager installed at a time, and once a Security Manager has
been installed it cannot be uninstalled (except by restarting the VM). Java-enabled applications
such as Web browsers install a Security Manager as part of their initialization, thus locking in the
Security Manager before any potentially untrusted code has a chance to run.

Usually a security exception propagates up through all the methods of the thread that made the
disallowed call. When the topmost method finally gets the exception, the thread exits. A thread that
exits this way prints the exception and the stack trace of all the methods that led to it. These are the
sorts of messages often cut and pasted in comp.lang.java.security by Java developer newbies
wanting help with security problems.

One important thing to realize about security exceptions is that it is perfectly possible for a hostile
applet to catch any security exceptions it generates in a try/finally block. So don't count on security
exceptions to tip you off to hostile applet activities. Purported Java security products that count
security exceptions can easily be thwarted by this strategy. A hostile applet can thus probe your
Security Manager with impunity. (Besides, a cracker who has done his homework will know in
advance what your Security Manager will allow and disallow, so he will never bother trying to
make a call that causes a security exception.)

The Security Manager is completely customizable (through subclassing), although applets are not
allowed to install Security Managers. A default Security Manager is provided (like the Class
Loader) as a template from Sun Microsystems. Each Java-enabled application fills in the template
(by subclassing the template class) to meet its own security requirements. The Java runtime library
is written so that all requests to perform dangerous operations are referred to the Security Manager.
Access checks are used for thread access, OS access, network access, and Java component access.

What the Security Manager Is Set Up to Do for Untrusted Applets

The Security Manager has the following duties:

• Prevent installation of new class loaders. The job of class loaders is to keep the namespaces
properly organized. Because security checks are requested by classes in the Java library,
applets must be prevented from spoofing the library classes.
• Protect threads and thread groups from each other. (Unfortunately, JDK 1.0 implementations
of this piece of policy do not function properly. Some malicious applets, discussed in
Chapter 4, have been written to take advantage of this. Better thread protection is provided in
JDK 1.1 and above.)
• Control the execution of other application programs.
• Control the ability to shut down the VM.
• Control access to other application processes.
• Control access to system resources such as print queues, clipboards, event queues, system
properties, and windows.
• Control file system operations such as read, write, and delete. Access to local files is strictly
controlled.
• Control network socket operations such as connect and accept.
• Control access to Java packages (or groups of classes), including access to security
enforcement classes.

Sun's Security Manager can be customized. The appletviewer, part of the JDK, reads a
configuration file with several user-determined security options. These configuration files declare
low-level details, such as "myfile.txt should be writable by applets." Such configuration files
appear again for use with JDK 1.1 and Java 2, allowing a user to define a security policy that
interacts with the code signing system.

The job of the Security Manager has been deeply affected by many of the new code-signing and
access-control features now found in the Java security architecture (see Chapter 3). Using
encryption-based authentication methods, the Security Manager in concert with other mechanisms
can set up much more sophisticated rules for trusted, partially trusted, and untrusted applets.

CHAPTER SECTION:9

Section 9 -- Different Classes of Security


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.

JDK 1.0

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.
Figure 2.8 There are three possible paths that Java byte code may follow
through the JDK 1.0.2/JDK 1.1 security models.

The path chosen depends on where the byte code originates and (in the case of
JDK 1.1) what signatures the code carries. External byte code (loaded across the
network) that is not signed by a trusted party (1) must be Verified and is subject to
the Class Loader and the Security Manager. Locally developed byte code (2) is
subject to the same checks (unless it is placed on the CLASSPATH). Byte code
from the JDK distribution (including other packages placed on the CLASSPATH)
and any class that is signed by a trusted party (3) does not pass through the
Verifier and may be subject to further optional security checks.

Built-In Classes: The Privileged Few


The JDK includes a class library that makes Java as powerful as it is. Since this code is part of Java
itself, it is considered trustworthy. The fact that built-in Java classes bypass security measures has
many implications. The system makes sense when you consider that this code is part of the Virtual
Machine itself. If the VM is loaded before the Verifier, there is no choice but to consider the built-
in Java classes trustworthy.

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.

Non-Built-In Classes: The Unwashed Masses


In JDK 1.0, all classes not considered built-in are subject to more strict security checks. Paths (1)
and (2) of Figure 2.8 both apply to non-built-in classes. Classes in path (1) include all applets
loaded from the network. Classes in path (2) include all applets loaded locally, but not specified in
the CLASSPATH. It is not possible to stress heavily enough how important it is to consider
carefully which classes you grant built-in status. If you install a Java class in your CLASSPATH it
will no longer be subject to security checks. Only completely trusted code should be given this
privilege.

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.

JDK 1.1

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.

Java 2

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.

Figure 2.9 In Java 2, all code is distinctly identified by


where it comes from and what signatures it carries.

Byte code may make calls to potentially dangerous


functionality. When such calls are made, the
AccessController (new to Java 2) consults policy and uses
stack inspection to decide whether to allow or disallow a call.
The distinction between applets and applications no longer
applies.

Chapter 3 is devoted to understanding the subtleties of this new approach to Java security.

CHAPTER SECTION:10

Section 10 -- Type Safety


The Java language is designed to enforce type safety. This means that programs are prevented from
accessing memory in inappropriate ways. More specifically, every piece of memory is part of some
Java object. Each object has some class. For example, a calendar-management applet might use
classes like Date, Appointment, Alarm, and GroupCalendar. Each class defines both a set of objects
and operations to be performed on the objects of that class. In our calendar management example,
the Alarm class might define a turnOn operation, but the Date class would neither need nor allow
turnOn. Type safety means that a program cannot perform an operation on an object unless that
operation is valid for that object.

Why Type Safety Matters

Type safety is the most essential element of Java's security. To understand why, consider the
following slightly contrived example. A calendar-management applet defines a class called Alarm.
This class is represented in memory as shown in Figure 2.10. Alarm defines an operation turnOn,
which sets the first field to true. The Java runtime library defines another class called Applet,
whose memory layout is also shown in Figure 2.10. Note that the first field of Applet is
fileAccessAllowed, which determines whether the applet is allowed access to files on the hard disk.

Figure 2.10 Type safety provides an important foundation for the


Java security model.

In this figure, two classes, Alarm and Applet, each include a number of
fields. Setting the first field in these classes to "true" is not equivalent.
Type safety checks ensure that any object a method may try to
manipulate is of the proper type.
Suppose a program tried to apply the turnOn operation to an Applet object. If the operation were
allowed to go ahead, it would do what turnOn was supposed to do, and set the first field of the
object to true. Since the object was really in the Applet class, setting the first field to true allows the
applet to access the hard disk. The applet would then be allowed (incorrectly) to delete files.

This example shows what can go wrong if type safety is violated. In our experience, every type
safety violation has created an opportunity for an untrusted applet to break out of Java's security
restrictions. Given the importance of type safety, the next section explains Java's strategy for
ensuring type safety.

How Java Enforces Type Safety

Every Java object is stored in some region of the computer's memory. Java labels every object by
putting a class tag next to the object. One simple way to enforce type safety is to check the class tag
of the object before every operation on the object. This will help make sure the object's class allows
the operation. This approach is called dynamic type checking.

Though dynamic type checking works, it is inefficient. The more time a system spends checking
class tags, the more slowly programs run. To improve performance, Java uses static type checking
whenever it can. Java looks at the program before it is run and carefully tries to determine which
way the tag checking operations will come out. This is more complicated, but more efficient than
dynamic type checking. If Java can figure out that a particular tag checking operation will always
succeed, then there is no reason to do it more than once. The check can safely be removed,
speeding up the program. Similarly, if Java can figure out that a particular tag checking operation
will always fail, then it can generate an error before the program is even loaded.

The designers of Java carefully crafted the Java language and byte code formats to facilitate static
type checking. The byte code Verifier is a very effective static type checker, eliminating almost all
of the tag checking operations from Java programs. The result is a type safe program that runs quite
efficiently.

Static type checking has other advantages, too. For example, static type checking can be done at
compile time, thus informing the software developer of any type errors before the code is shipped.

Type Confusion

There is only one problem with Java's static type checking strategy: It's complicated. Though Java's
designers obviously got the overall strategy right, there are a great many details that have to be
perfect for type safety to be enforced. An error in any of these details would be a tiny but crucial
hole in Java's type safety enforcement dike.

A clever cracker who finds such a hole can launch a type confusion attack. Such an attacker could
write a Java applet designed to trigger a tiny type enforcement error. The attacker could then create
a situation like our Alarm/Applet example in which the program has one kind of object but Java
thinks that object has some other kind. As in the example, this seemingly harmless confusion can
be exploited to breach Java's security. If you recall that two of the three parts of the base Java
security sandbox are themselves Java classes, it becomes immediately apparent what sorts of havoc
type confusion can cause. Several real-life type confusion attacks are discussed in Chapter 5.

Type Safety and Encapsulation

Type safety is the cornerstone of Java security. There is much more to the rest of the edifice, of
course, but without type safety the entire building would be unsound.

Type safety guarantees that programs will not do terrible and dangerous things such as treating
pointers as integers (or vice versa) or falling off the end of an array. These are the sorts of things
that make it very easy to write insecure code in C and C++.

The typing constraints in Java exist to prevent arbitrary access to memory. This in turn makes it
possible for a software module to encapsulate its state. This encapsulation takes the form of
allowing a software module to declare that some of its methods and variables may not be accessed
by anything outside the code itself. The more control is placed on access points (and the fewer
access points there are), the better a module can control access to its state.

It is this idea that permeates the design of the Security Manager. The VM controls access to
potentially dangerous operating system calls by wrapping the calls in an API that invokes a security
check before making the call. Only the VM can make a direct system call. All other code must call
into the VM through explicit entry points that implement security checks.

As we will see in the next chapter, encapsulation turns out to be essential to the design of the Java 2
access control system as well.

CHAPTER SECTION:11

Section 11 -- Browser-Specific Security Rules


Security Manager, are meant to be defined by application authors (including browser vendors)
through subclassing. The rules that Web browsers enforce make up the security policy defined by a
particular vendor. Ultimately, Netscape could decide to implement different rules than Microsoft.

Of course, every browser's security implementation depends entirely on the rest of the security
model being properly defined. Critical pieces of the Java environment directly affecting security
include the Java VM, and the built-in libraries. If any of these pieces have errors, the entire security
system will break, regardless of which vendor does the other parts.
Netscape Navigator and Microsoft Internet Explorer

All Netscape Navigator versions subsequent to 2.0 are Java enabled. All Microsoft Internet
Explorer versions subsequent to 3.0 also include Java. It is, however, very easy to disable Java in
both kinds of browsers as long as you can find the right dialog box (see Chapter 4). There is a
toggle switch for the entire Java environment. In both Netscape Navigator and Internet Explorer,
Java is enabled by default.

The two browsers' security policies are, at the present time, very similar. Both are somewhat strict.
The following rules apply to all untrusted applets running under Netscape Navigator and Internet
Explorer:

• Applets cannot read or write files locally.


• Applets cannot open a client-side network connection to any machine other than the applet's
origin host. Applets after JDK 1.1 can open a server socket as long as the port number is
greater than the privileged port number on the machine (usually 1024).
• Applets can read only nine system properties. This allows an applet to access information
such as the vendor who created the Java VM, the VM version number, the file separation
character (either \ or /), the character used to separate lines, and so on. Applets are not
permitted to read any other system properties.
• If an applet is loaded using the file: URL, and it does not reside in a directory in
CLASSPATH, it is loaded by an Applet Class Loader.

There is no reason that all future browser vendors will choose to implement similar security
policies. That two major vendors now do so is probably an artifact of Java's short history. Once
browsers begin to implement different policies, security issues will become more complex.

We can see a split among vendors happening already with the 4.x versions of Netscape Navigator
and Internet Explorer. Both products offer complex security models based on digital signatures and
partial trust. Although the models are quite similar to each other and to the model defined by Java
2, there are many detailed differences that annoy developers and users.

One side effect of the common ground between different vendors' Java security implementations
was that early on in Java's history, security holes often cut across all Java implementations. That is,
the same problem would be exploitable in all Java VMs, regardless of the browser. These days, it is
more common for errors to be relegated to one browser or another. We have more to say about
vendors, implementations, and security holes inChapter 5.

CHAPTER SECTION:12

Section 12 -- The Fundamental Tradeoff


In Chapter 1, we pointed out that there is no such thing as 100-percent security. Where security is
concerned, there is a fundamental tradeoff between power (functionality) and security. Java
attempts to be both powerful and secure. Java is trying to manage complex security issues
proactively. Despite such efforts, some flaws in the model still need to be addressed.

Functionality and security will always exist in an inverse relationship. Currently, Net users choose
functionality over security. Java designers are attempting to increase security without paying too
high a price in functionality.

CHAPTER SECTION:13

Section 13 -- Is There Really a Java Security Policy?


The base Java Security sandbox is comprised of three major components: the byte code Verifier,
the Class Loader, and the Security Manager. Each of these components must work properly in
order for Java to perform in a secure fashion. The Security Manager depends on Class Loaders to
correctly label code as trusted or untrusted. Class Loaders also shield the Security Manager from
spoofing attacks by protecting local trusted classes making up the Java API. On the other hand, the
class loader system is protected by the Security Manager, which ensures that an applet cannot
create and use its own Class Loader. The Verifier protects both the Class Loaders and the Security
Manager against language-based attacks meant to break the VM. All in all, the three parts
intertwine to create a default sandbox. However, the three parts are not created or specified by a
standards committee. Java applications, including Java-enabled Web browsers, are allowed to
customize two of the fundamental portions of the security model to suit their needs (the Class
Loader and the Security Manager).

The security model is much more distributed than many computer security experts would like
[Badger and Kohli, 1995]. In the end, a great deal of faith is placed in the ability of VM
implementations to ensure that untrusted code remains properly contained. Bugs in the system will
compromise the entire security model.

In 1996, we complained that there was no formal, high-level security model in Java. Instead, the
security policy is derived from a collection of low-level detail checking. This is difficult because
without a formal model it is impossible to say what secure means. With no standardized model,
each vendor is free to define the term secure. In addition, no particular implementation can be
verified. A little progress towards a more formal description of the security model was made when
Sun commissioned a report entitled Security Reference Model for JDK 1.0.2 [Erdos, et al, 1996].
This sort of work is what users of security-critical consumerware should demand. Unfortunately,
the SRM is now seriously out of date. Once again, Internet-time-compressed development
schedules have outstripped the need for rigorous security design and analysis. This is a common
trend in consumer software, and one that is destined to flame out under the rigorous security
demands of e-commerce.
Programming language researchers have also done some work trying to prove the soundness of the
Java language and the VM definition [Drossopoulou and Eisenbach, 1998; Stata and Abadi, 1998].
Although this work is still in preliminary stages, some interesting and suggestive results are
available. The bottom line is that the definition of Java will probably turn out to be sound, once
many more details have been filled in.

The Java runtime system is large (upwards of 28,000 lines of code, not including the VM). This
raises important security assurance questions. Generally speaking, programs as large and complex
as that are extremely hard to verify. It is common knowledge that buggy software causes many
security vulnerabilities [Garfinkel and Spafford, 1996]. There is no centralized authority ensuring
that Java's security code is bug free.

The Java security system has seen the benefit of external peer review (some solicited and some
not). JavaSoft has put together a Security Advisory Board (including one of the authors) and now
includes prominent security researchers among its staff. Outsiders have done their best to keep
security claims honest, and a number of security researchers (including the authors of this book) are
spending a fair amount of effort still trying to find its weaknesses. Later in the book, we discuss
some of the problems researchers have found, and what Java vendors are doing to address them.
Before we dig into that topic, we introduce the new security mechanisms found in Java 2 and
discuss why the Java security model is a completely new beast.

Beyond the Sandbox


Signed Code and Java 2
Java has outgrown the original restrictive sandbox. The anticipated future of mobile code security,
a complex mix of sandboxing and code signing, is now upon us with Java 2. In essence, the three
parts of the sandbox explained in the previous chapter implement a language-based security
enforcer. This enforcement model has been hybridized and expanded to include fine-grained
notions of trust and permission built on digital signatures. That means major changes to Java
security. This chapter centers on those changes.

Chapter 1, "Mobile Code and Security: Why Java Security Is Important," briefly introduced the
notion of code signing and mobile code policy through the discussion of ActiveX. The ActiveX
trust model is suited only to run completely trusted code. At the core of that kind of trust model is a
black-and-white decision either to trust the code or not. Such a decision can be influenced by
determining who vouches for the code. Digital signatures are used for the vouching.

Java's approach to trust is also based on digital signatures. However, instead of allowing only
black-and-white trust decisions à la ActiveX, Java 2 allows fine-grained access control decisions to
be made. With the introduction of code signing in 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. Java 2 further complicates the picture
with the addition of access control.

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 have
complained about Java's restrictive sandbox. Writing code that works within the tight restrictions of
the sandbox is a pain, and the original sandbox is very restrictive.

The addition of code signing to Java complicates things. As it now stands, the Java sandbox has
been reduced to a default. The whole game has changed. Tracing the history of this change as we
do in this chapter can lend some important perspective.

Before we dig into the complex issues of code signing and trust models, it does us good to review
what it is we're trying to achieve in the first place. After all, the point of all this highfalutin'
architecture is not to make the world's most complicated system. The real objective is securing
mobile code.

After we remind ourselves of the main goal of the new security model, we are ready to trace its
evolution. We will begin by explaining the enhancements added to Java with the release of JDK
1.1, and go on to discuss the Java 2 model in detail.

Chapter Three Sections


1. What's the Main Goal?
2. Security Enhancements in JDK 1.1
3. Signed Code
4. Trust
5. An Introduction to Java 2 Security
6. Access Control and Stack Inspection
7. New Security Mechanisms in Sun's Java 2
8. Outside the Sandbox

Beyond the Sandbox: Signed Code and Java 2


CHAPTER SECTION:1

Section 1 -- What's the Main Goal?


Everyone agrees that code signing makes the Java security model a lot more complicated, not to
mention actually using the new system. Where security is concerned, complexity is bad since it
increases the odds of an error in the system's design or implementation. If we're going to add all of
this complexity, what exactly is it that we are gaining? What's the main goal?

The main goal is to gain better control over the security of mobile code. We can achieve this goal
by winning the battle on three fronts. By adding code signing and expanding beyond a black-and-
white trust model, we hope to gain:

1. The ability to grant privileges when they're needed.


2. The ability to have code operate with the minimum necessary privileges.
3. The ability to closely manage the system's security configuration.

We can judge the JDK 1.1 and Java 2 security models by how well they meet these objectives.

The first objective is simple: We want to give trusted code the privileges it needs to get its job
done. A word-processing applet needs the ability to read and write files, so we want to grant this
privilege if we have enough faith that the applet won't misbehave. In general, users want to be able
to grant any privileges at all to any code they choose, as long as the benefits of doing so outweigh
the risks.

The second objective is to have code that operates with the minimum necessary privileges at all
times. Security experts call this the "principle of least privilege." This is a common-sense idea-why
use a chain saw when a butter knife is sharp enough for the job-but it has profound implications if
we carry it to its logical conclusion. One simple implication is that we want programmers to have a
way to renounce their privileges when they aren't needed and reenable the privileges when they are
needed. The principle of least privilege can be applied in many places throughout the system:

• We want to grant each applet or application the minimum privileges it needs.


• Rather than assigning a given applet's entire collection of privileges to all of its classes, we
want each class to get just what it needs.
• We want a class's privileges to be "turned off" except for brief periods of time.
• We even want to reduce the privileges of some of the built-in system classes.

The third objective is manageability. This is a tricky one. Some might think that the ultimate in
management power is when all possible options are presented to the user. (Power users, developers,
and other gurus tend to think along these lines.) But in reality, users are overwhelmed and irritated
when they are confronted with too many big complicated dialog boxes. Somehow the choices must
be boiled down so that users get just the control they need without being asked any unnecessary
questions.

As we see in the rest of this chapter, the Java security model is still a work in progress. Nobody
knows yet how to achieve all of these goals, or even how they trade off against each other. Today's
models are pretty good, but they are a far cry from perfect.

CHAPTER SECTION:2
Section 2 -- Security Enhancements in JDK 1.1
JDK 1.1 appeared in the early Spring of 1997 and included a number of improvements and changes
to the base Java security model of JDK 1.0.2. Fortunately, none of the material about the base Java
security model covered in the last chapter (or for that matter, things you learned from the previous
edition of this book) was outdated or replaced; rather, the Java security architecture was changed
through a process of enhancement and addition.

From a security perspective, the most important changes introduced in JDK 1.1 were the addition
of authentication and simple access-control mechanisms that rely on the use of cryptography.
Remember, security is much more than just cryptography. Think of cryptography as a means to an
end-an important part of the puzzle, but only a part. A side effect of the need for cryptographic
functionality inside the model itself was the creation of a crypto API. The crypto API, also
introduced with JDK 1.1, provides a basic toolkit of cryptography algorithms that developers can
use in their programs.

The Crypto API

Today, Java includes a cryptography toolkit that includes both an API and some packages
implementing a portion of the functionality behind the API. Classes in the java.security package,
the package implementing the cryptographic functionality, have a dual purpose. One purpose is to
provide the cryptographic methods that Java's designers used to implement the JDK 1.1 and Java 2
security models. The second purpose is to provide cryptography functionality to Java developers
charged with creating secure applications.

Parts of a crypto API were released with JDK 1.1. The parts included both one-way hash functions
and digital signature capability. DES encryption tools were released only as an extension to North
American users.

Encryption tools and their mathematically related cousins (such as digital signing) change the way
Java use policies are managed. Digital signatures, which are discussed next, make it possible to
authenticate who has vouched for a piece of code, and potentially check it for tampering. If you
decide to trust a particular person, you can set things up so that you automatically trust programs
that person signs. (Note that with the right tools, anyone can sign any piece of code. Whether or not
a piece of code is written, released, or supported by the person who signed it is not something
digital signatures can tell you.) Because the signature is a mechanism for vouching and spreading
trust around, if you trust some experts in the field who agree to approve Java programs based on
their analysis, you can trust any code that they sign as well. Digital signing paves the way for a true
community of trust to develop. We think digital signing is important enough to warrant an entire
section itself. See page 88.

Beyond digital signatures, the crypto API released with JDK 1.1 includes a couple of other
capabilities. One-way hash functions provide a way to fingerprint a program or data so that you can
verify that it has not been changed since being created. Fingerprinting hash functions such as MD5
and SHA make distribution over the Net easier to swallow. If you are certain that a program you
are downloading from the Net is the original program (and not a Trojan Horse masquerading as the
original), you will probably be more likely to use it. Many archives on the Web today make use of
MD5.

Fingerprinting, also called message digesting, works by performing a one-way hash over a series of
bytes. Given a program (which is really just a bunch of ones and zeros), it is possible to compute a
hash that ends up being many times smaller than the original program, but (hopefully) represents
only that program. The main trick is to avoid collisions, whereby the same fingerprint is computed
for different programs, and to come up with a hash function that can't be run in the opposite
direction. MD5 and SHA are systems for computing one-way hashes over a binary file. The crypto
API provides a way for Java programs to include this functionality.

MD5 and SHA are useful when it comes to signing code because the act of signing is actually a
complicated function of a secret crypto key and the data to be signed. The math is hairy enough that
it is a much better idea to compute it using a program's hash instead of the program itself.
Remember, the hash is many times smaller than the program it represents. Figure 3.1 shows the
important role that one-way hash functions play in code signing.

Figure 3.1 How code is digitally signed (A) and digital signatures are
verified (B).

(A) Signing code takes several distinct operations: (1) a one-way hash
calculation is run on a piece of binary code, resulting in a small "thumbprint" of
the code; (2) the hash is signed using the signer's private key; (3) the signed
hash and the original binary code are placed together (potentially along with
other signed and unsigned code) in an archive JAR. Now the JAR can be
shipped around as mobile code.
(B) Validating signed code also takes several steps: (1) a piece of binary code
and its associated signed hash are removed from the JAR; (2) a new hash is
calculated using the same one-way hash algorithm that the signer used to create
the signed hash; (3) the signature carried by the signed hash is cryptographically
validated with the signer's public key (possibly with reference to certificate
authorities and trust chains); (4) if the signature checks out, the now decrypted
original hash is available for comparison with the new hash. Though all three
Java code signing schemes (Sun, Microsoft, and Netscape) share these two
processes, there are enough differences that the systems do not inter-operate.
See Appendix C for examples of how to sign Java code under each
implementation.

Another function that appeared as part of the crypto API (at least in the package available only in
the United States, and known as the Java Cryptography Extension, or JCE) was DES encryption.
DES, an acronym for Digital Encryption Standard, is a venerable old encryption algorithm that can
in some cases be deciphered (given enough effort and a small enough key). DES is certainly much
more secure than plain text, but does not provide the best available security. In 1998, the EFF
created a special-purpose machine to crack DES messages. The purpose of the machine was to
emphasize just how vulnerable DES really is. (For more on the DES cracker,
see www.eff.org/descracker/.)

Most Unix machines use a variant of DES to encrypt user passwords stored in the /etc/passwd file.
If 56-bit (or smaller) keys are used for DES, then the U.S. government will allow its export and use
outside the United States. There is also a variant called triple DES that effectively has a 112-bit
key, which will be safe against brute-force searching for a long time. The ease of "breaking'' DES is
directly related to the length of its key.

Certificates

Another feature that appeared in JDK 1.1 is certificate technology based on the X.509v3 open
standard. Certificates provide an authentication mechanism by which one site can securely
recognize another. Sites that recognize each other have an opportunity to trust each other as well.
When a secure socket layer (SSL) connection initializes between two machines, they handshake by
exchanging certificates. SSL is discussed in the next section.

A certificate is a piece of identification (credential) much like a driver's license. Information stored
inside a typical certificate file includes the subject's name, the subject's public key, the certificate's
issuer, the issuer's digital signature, an expiration date, and a serial number.

So the question is, who gives out these certificates? Someone (or some thing) called a certification
authority (CA). There are a handful of companies that have set themselves up as CAs in the world.
These include Netscape, GTE, Verisign, and a few others. But why should you trust them? Good
question. (See page 92.)

Secure Communication

Java 2 now includes a package for secure socket layer (SSL) communication. Similar to Netscape's
SSL, the Java SSL provides a secure communications channel by using encryption. SSL works by
providing a mechanism for encrypting packets on the sending end, sending them over an untrusted
channel, and decrypting them at the receiving end. SSL is useful for many business applications,
including the transmission of proprietary information and electronic currency.

Most Web servers and browsers now support SSL, allowing a browser to communicate with a Web
server without anyone else overhearing the conversation. (Well, an outsider might overhear a
conversation, but he or she certainly won't understand it.) Though SSL is commonly used over the
Web, it can actually be used to protect virtually any sort of network transaction.

Most browsers support SSL by providing a "Secure HTTP Connection" service that looks to the
user just like a normal Web connection, but uses SSL underneath. This allows you to reap the
benefits of SSL without having to learn anything except how the browser tells you whether a
connection is secure.

The encryption technology underlying SSL is generally believed to be secure, but there are two
potential problems. First, the U.S. government restricts the export of strong cryptography software.
If your browser version includes dumbed-down exportable cryptography software, your
communications might not be as secure as you think. Second, SSL is good at providing secure
communications, but it is not as good at establishing who you are communicating with. This leads
into all the problems of authentication and key distribution discussed on page 90.

CHAPTER SECTION:3

Section 3 -- Signed Code


The capability to digitally sign Java byte code (at least byte code files placed in a Java archive,
called a JAR file) was introduced with JDK 1.1 and greatly expanded with Java 2. Digital signing
capability is an important part of the new Java security regimen. This is exciting because digital
signing radically alters the amount of trust you can place in a piece of code. A Tutorial on signing
Java code with the current tools from Microsoft, Netscape, and Sun can be found in Appendix C.

One particular kind of cryptography tool allows a chunk of digital information (including, of
course, Java byte code) to be signed by a person or organization. See Figure 3.1. Because a digital
signature has special mathematical properties, it is difficult to forge. Your browser can verify a
signature, allowing you to be fairly certain that a particular person or organization vouches for the
code. That means you can instruct your browser always to accept applets signed by some party that
you trust, or always to reject applets signed by some party that you don't trust. Same thing goes for
a nonbrowser-based VM, which can be instructed (through policy) how to treat application code
signed by particular entities.

It is important to recognize that even if you know exactly which Web pages you are visiting and
who created them, you probably don't know who wrote each applet that appears on the pages you
visit. Applets are shuffled around on the Net like Beanie Babies in a fifth-grade classroom.
Contrary to popular belief, you don't always know where information is coming from on the
Internet. A nasty attack called IP spoofing allows a bad guy to send you network traffic that claims
to come from someplace else. For instance, you might think the traffic is coming from
"whitehouse.gov", when it's really coming from "cracker.org". IP spoofing used to be considered
just a theoretical possibility, but it has actually happened in recent years. The best-known example
is an attack by the infamous cracker Kevin Mitnick on a machine managed by computer security
worker Tsutomu Shimomura. Mitnick's attack led to his eventual capture and conviction
[Shimomura and Markoff, 1996].

An attack known as Web spoofing shows that even in the absence of IP spoofing, it is not always
clear that you are visiting the site you may think you're visiting [Felten, et al., 1997]. An attacker
can lure you into a "false Web" that looks just like the real one, except that the attacker can see
everything you do, including anything you type into a form, and the attacker can modify the traffic
between you and any Web server. All of this is possible even if your browser tells you that you
have a "secure" connection. See Figure 3.2.

Figure 3.2 A Web Spoofing attack can be carried out with extensive use of a
browser's mobile code capability.

The Princeton Team has implemented a demonstration of Web Spoofing that


makes extensive use of JavaScript. Once an attacker has lured the victim to the
attack server (shown as www.attacker.org), the attacker can control the victim's
view of the Web by acting as a rewriting proxy. Clever use of JavaScript makes all
changes invisible to the victim and can even appear to offer encrypted traffic.

Even if you ignore the possibility of spoofing, using the return address of an applet (that is,
knowing the Web site where you got the applet code) still isn't good enough to base a trust decision
on. A digital signature holds much more information. For example, such a signature could tell you
that although the applet is being redistributed by a site you don't trust, it was originally signed by
someone you do trust. Or it can tell you that although the applet was written and distributed by
someone you don't know, your friend has signed the applet, attesting that it is safe. Or perhaps it
can simply tell you which of the thousands of users at aol.com signed the applet.

Digital Signatures

So how do you sign a piece of code? The key to certification and authentication is the use of digital
signatures. The idea 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. In order to be
useful, a digital signature should satisfy five properties [Schneier, 1995]. It should be:

1. Verifiable: Anyone should be able to validate a signature.


2. Unforgeable: It should be impossible for anyone but you to attach your signature to a
document.
3. Nonreusable: It should be impossible to "lift'' a signature off one document and attach it to
another.
4. Unalterable: It should be impossible for anyone to change the document after it has been
signed, without making the signature invalid.
5. Nondeniable: It should be impossible for the signer to disavow the signature once it is
created.

Mathematicians and computer scientists have devised several digital signature schemes that appear
to work quite well. The full details are very technical. If you're interested in learning more about
such schemes, Bruce Schneier's excellent book, Applied Cryptography, is a good place to start
[Schneier, 1995].

The digital signatures used for Java code are based on public-key cryptography. If Alice wants to
be able to sign documents, she must first use a special mathematical technique to generate two
large numbers: her own private key, and her public key. As the names suggest, Alice keeps her
private key to herself. Keeping it secret is essential. Her public key, however, is announced to the
world.

Alice's private key is used for signing electronic documents. Her public key is used to verify those
signatures. See Figure 3.1. Anyone who knows the private key (hopefully only Alice!) can run a
special computation involving the document and Alice's private key. The result of this process is a
digitally signed version of the document.

Anyone who knows Alice's public key can verify her signature by running a special computation
involving the signed document and Alice's public key. Since only Alice knows the private key, she
is the only one who can put her signature on documents. Since everyone knows her public key,
anyone can verify that the signature is hers.

Everything sounds great. You tell your browser to trust applets signed by Alice by registering
Alice's public key. Whenever applets claim to come from Alice, the browser can verify that claim
by comparing the registered public key to the signed applet. If the applet is not from Alice, it can be
rejected.

Key Distribution

But how do you know what Alice's public key is?

If you know Alice, she can call you on the phone and tell you her public key. In this case, you will
know the key is valid because you recognize Alice's voice. This doesn't work if you don't know
Alice. How do you know the person on the other end of the phone is Alice? Maybe it's Alice's evil
twin Zelda, trying to pass off Zelda's public key as Alice's so she can forge Alice's signature.

One way around this problem is to ask Alice's twin brother Allan to help. Alice can create a
document containing her public key and have Allan sign that document. If you trust Allan and you
know Allan's public key, then the document tells you reliably what Alice's public key is.

But how do you know Allan's public key? You can't ask Alice and Allan to vouch for each other's
public keys, because Zelda could create a false Alice key and a false Allan key and use them to sign
documents vouching for each other! This leaves us stuck with a chicken-and-egg problem.

The usual solution is to use a certification authority (CA). The CA, Claire in our example, is in the
business of certifying keys. Alice goes to the CA's office with her birth certificate, passport, driver's
license, and DNA sample. Once she has convinced Claire that she really is Alice, she tells Claire
her public key, and Claire signs an electronic document that contains Alice's public key. That
document serves as an electronic credential for Alice.

After Alice has a credential, key distribution is much easier. Alice can plaster copies of her
credential everywhere: on bulletin boards, on her homepage, and at the end of every email message
she sends. Better yet, whenever Alice signs a document, she can attach a copy of her credential to
the signed document. On receiving the document, you can first check the credential by verifying
Claire's signature, and then verify Alice's signature using the public key included with the
document. Zelda can't trick you into accepting a bogus public key for Alice, because she can't forge
Claire's signature. Figure 3.3 shows the process by which a signature on a piece of signed code can
be validated.

Figure 3.3 Validating a signature on signed code.

In this example, a piece of code is signed by the private key of thing1.


The corresponding public key, available on thing1's certificate can be
used to validate the signature carried by the code. For added security
and to make key management more reasonable, browsers typically
validate the CA signature carried on the certificate.

The beauty of this approach is that if everyone can visit Claire and get a credential, then no one has
to remember any keys except for his or her own private key (to sign documents), and Claire's
public key (to verify credentials). There are still two problems, though. Everyone must trust Claire.
As the authority, she can impersonate anyone. And you still need a reliable way to get Claire's
public key. It doesn't help to have Claire get a credential from Claire's mom, Elena. You would
have no more reliable way of knowing who Elena is.

There is no technological solution to this. Claire's key will probably be hard-wired into your
browser software, or entered by support staff at install time. As long as you get a valid copy of the
browser, and no one has messed with your hard disk, everything will be okay. How do you know
you have a valid copy of the browser? It will be signed by the browser vendor. How do you know
the browser vendor's signature is valid? Don't ask-there lies madness.

What Signing Can't Do

Even if the signing and signature-checking mechanisms work perfectly and are able to reveal who
signed each applet, a huge unsolved problem still remains. Technology can tell you who signed an
applet, but it can't tell you whether that person is trustworthy. That's a decision you have to make
based on human judgment. And you'd better make the right decision.

CHAPTER SECTION:4

Section 4 – Trust
Once a code signing infrastructure is in place, you will be able to know reliably who vouches for
each Java program. The next link in the chain is figuring out what to do with that knowledge.

One thing you can certainly do is to relax Java's security rules for applets that you trust. For
example, with the default sandbox Java normally prohibits any access to files in order to prevent an
applet from corrupting your hard drive or reading your private data. If you trust applets from
particular sources, though, you might want to allow them to read files. Introducing permissions
granted according to trust level opens up vast new application areas, including things like
spreadsheet applets, games with stored high scores, Web sites that recall your preferences, a host of
different remote management possibilities, and so on.

Besides access to files, there are many other capabilities you might want to grant a trusted applet or
application: access to your machine's microphone and camera, freedom to make network
connections, and maybe even freedom to label other code as trusted. It all depends on your decision
to trust and how much to trust a signed program. There are several ways you can make these
decisions.

Who Do You Trust?

The first decision is whether to use a black-and-white or a shades-of-gray policy. A black-and-


white policy is one that divides all programs into two groups: trusted and untrusted. This was the
only sort of trust policy that was easy to implement using JDK 1.1. Java 2, however, changed all
that. Java 2 makes it possible to create a shades-of-gray policy, allowing you to assign any degree
of partial trust to a Java program. (Recall Figure 1.6 in Chapter 1.)

Before Java came along, most Internet software worked on a black-and-white model. If someone
offered to let you download a program, you had two choices: either you downloaded the program
or you didn't. If you did, you were trusting the program completely since there was nothing to stop
it from running wild on your machine. If you didn't download the program, you were treating it as
completely untrusted. Java, with its security policies as implemented in the base sandbox, changed
the rules a bit by making it easier to decide what to download in the first place. If an untrusted
applet can't bite you, you might as well check it out.

The black-and-white model is sometimes called the shrink-wrap model because it's similar to
software you purchase. If you buy a software package from a reputable software store, you can
reasonably assume that the software is safe to load on to your machine. People who use the term
shrink-wrap model tend to assume that no one would ever want to run software that wasn't written
by a large software company. We don't agree with that implication, so we'll stick with the term
black-and-white.

It might seem that the shades-of-gray model is better than the black-and-white model, because
black-and-white only allows you to label programs as completely trusted or completely untrusted.
On the other hand, shades-of-gray gives you more choices. You may still label an applet as
completely trusted or completely untrusted if you wish.

Choices are not always good, as anyone who has encountered the cereal aisle of a large
supermarket can attest. Making choices takes up time that you would probably rather spend doing
something else. Frequent decision-making saps your attention span, so you are more likely to make
a mistake, thus opening yourself up to attack. Finally, having more options saddles your browser
with more complicated record-keeping duties to keep track of all of your decisions. This extra
complexity might lead to bugs in the browser, possibly jeopardizing security yet again.

Which model is better, black-and-white or shades-of-gray? It depends on how people react to the
two systems, which is hard to predict. Mostly likely, competing browsers will offer different
models, and the models will fight it out in the marketplace. The decision is ultimately one of user
preference.

Free the Trusted Code!

Once you've decided who to trust, the next issue is what you allow trusted programs to do. If you're
using the black-and-white model, then you have to decide whether to allow untrusted programs,
like applets off unknown Web sites, to run at all. You also have to decide what extra capabilities, if
any, you want to give to trusted programs. You might decide to let trusted programs do whatever
they want, with no restrictions at all. Or you might decide to run trusted programs under the
restrictive Java security rules of JDK 1.0.2. The choices depend on your taste for risk, and what
kinds of programs you want to run. With black-and-white security, however, all the programs you
trust receive the same level of trust.

If you're using a shades-of-gray model, you face more choices. You may decide on a program-by-
program (or signer-by-signer) basis exactly which capabilities to grant. Rather than presenting you
with a huge laundry list of possible capabilities for each program and forcing you to tick items off
the list, a good browser will probably provide a way for you to grant certain prepackaged sets of
capabilities. For example, there might be a set of permissions for videoconferencing applets, which
would include things like permission to use the camera, the microphone, the speaker, the display,
and networking access. Perhaps there would be another set of document-editing applet permissions,
which would include file-creation, file-reading, and file-modification capabilities.

There are two basic ways to group the mapping of program (or programs) to permission (or
permissions). Microsoft's Authenticode system, introduced in Chapter 1, defines security zones,
which are ways of grouping programs together. For example, all programs from a company intranet
signed by the system administrator's key might comprise a zone. (These zones might well involve
multiple keys and origins.) Policies can then be defined on a per-zone basis. Netscape defines
macro targets, which are groups of permissions (as sketched in the previous paragraph). For
example, a macro target might be called "typical game privileges" and define the permissions
typically needed by a network-enabled game.

Sun has a system of implication in which permission for code to use one resource can imply
permission to use another resource. In their model, each resource is required to define an implies()
method that can be used to ask a resource whether it implies a particular other permission. (More
detail is provided later in this chapter.)

All of these are examples of grouping signers or privileges together and treating the group as a unit.
Grouping is generally a good idea in security management because it reduces the number of
decisions that the user (or other policy-maker) faces. Fewer decisions means more attention paid to
each decision and hence, better decisions.

JDK 1.1, which introduced the concept of a signed applet, provides a black-and-white model. A
digitally signed applet can be treated as trusted local code as long as the signature key is recognized
as trusted by the system finally running the code. Java 2 provides a shades-of-gray model.

CHAPTER SECTION:5

Section 5 -- An Introduction to Java 2 Security


Signatures alone don't provide the infrastructure needed to allow Java code out of the sandbox
gradually. Access control mechanisms are required as well. 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
(without an inordinate amount of extra programming). There is no notion of access control beyond
the one-and-only trust decision made per class. That means in practice, JDK 1.1 offers a black-and-
white trust model much like ActiveX (although with the clear advantage that untrusted code must
stay in the sandbox).

The new security architecture in Java 2 has four central capabilities [Gong and Schemers, 1998]:
Fine-grained access control: The ability to specify that code with proper permissions is
allowed to step outside the sandbox constraints gradually (for example, an applet signed by a
trusted key might be allowed to open arbitrary network connections).

Configurable security policy: The ability for application builders and Java users to configure
and manage complex security policies.

Extensible access control structure: The ability to allow typed permissions and to group such
permissions in logical, policy-oriented constructs.

Security checks for all Java programs: A departure from the concept that built-in code
should be completely trusted. (It is this capability that serves to erase the once-important
distinction between applets and applications.)

It is important to note that the first three of these four capabilities are not really new to Java. Java is
a powerful programming language, and it has always been possible to implement complex,
configurable, extensible security policies based on fine-grained access control. It was just
exceptionally tricky. Java 2 serves to make this task possible for mere mortals.

A View from 50,000 Feet

At its heart, the Java 2 security model has a simple idea: Make all code run under a security policy
that grants different amounts of privilege to different programs. While the idea may be simple, in
practice, creating a coherent policy is quite difficult. Figure 3.4 shows the role that mobile code
identity and policy play in Java 2.

Figure 3.4 Mobile code in Java 2 interacts with user


defined policy through the AccessController.

Byte code may make calls to potentially-dangerous


functionality. When such calls are made, the
AccessController (new to Java 2) consults policy and uses
stack inspection to decide whether to allow or disallow a call.
Decisions are based on the identity of the code.

Java 2 code running on the new Java VMs can be granted special permissions and have its access
checked against policy as 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 Java 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." (Recall the dancing pigs
of Chapter 1.)

Executable code is categorized based on its URL of origin and the private keys used to sign the
code. The security policy maps a set of access permissions to code characterized by particular
origin/signature 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!

Code can be signed with multiple keys and can potentially match multiple policy entries. In this
case, permissions are granted in an additive fashion.

A Simple Example
An easy example of how this works in practice is helpful. First, imagine a policy representing the
statement "code from "www.rstcorp.com/" applet signed by 'self' is given permission to read and
write files in the directory /applet/tmp and connect to any host in the rstcorp.com domain." Next, a
class that is signed by "self" and that originates from "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 (more on this later).

CHAPTER SECTION:6

Section 6 -- Access Control and Stack Inspection


The idea of access control is not a new one in computer security. For decades, researchers have
built on the fundamental concept of grouping and permissions. The idea is to define a logical
system in which entities known as principals (often corresponding one to one with code owned by
users or groups of users) are authorized to access a number of particular protected objects (often
system resources such as files). To make this less esoteric, consider that the familiar JDK 1.0.2 Java
sandbox is a primitive kind of access control. In the default case, applets (which serve as principals
in our example) are allowed to access all objects inside the sandbox, but none outside the sandbox.

So what we're talking about here is a way of setting up logical groupings. Then we can start talking
about separating groups from each other and granting groups particular permissions. Security is all
about separation. Readers familiar with the Unix or NT file system will see clear similarities to the
notion of user IDs and file permissions.

Sometimes a Java application (like, say, a Web browser) needs to run untrusted code within itself.
In this case, Java system libraries need some way of distinguishing between calls originating in
untrusted code and calls originating from the trusted application itself. Clearly, the calls originating
in untrusted code need to be restricted to prevent hostile activities. By contrast, calls originating in
the application itself should be allowed to proceed (as long as they follow any security rules that
the operating system mandates). The question is, how can we implement a system that does this?

Java implements such a system by allowing security-checking code to examine the runtime stack
for frames executing untrusted code. Each thread of execution has its own runtime stack (see Figure
3.5). Security decisions can be made with reference to this check. This is called stack inspection
[Wallach, et al., 1997]. All the major vendors have adopted stack inspection to meet the demand for
more flexible security policies than those originally allowed under the old sandbox model. Stack
inspection is used by Netscape Navigator 4.0, Microsoft Internet Explorer 4.0, and Sun
Microsystems' Java 2. (Interestingly, Java is thus the most widespread use of stack inspection for
security ever. You can think of it as a very big security-critical experiment.)

Figure 3.5 Each Java program


thread includes a runtime stack
that tracks method calls.

The purpose of the stack is to keep


track of which method calls which
other method in order to be able to
return to the appropriate program
location when an invoked method
has finished its work. The stack
grows and shrinks during typical
program operation. Java 2 inspects
the stack in order to make access
control decisions. In this example,
each stack frame includes both a
method call and a trust label (T for
trusted, U for untrusted).

Simple Stack Inspection

Netscape 3.0's stack-inspection-based model (and every other black-and-white security model) is a
simple access control system with two principals: system and untrusted. Just to keep things simple,
the only privilege available is full.

In this model, every stack frame is labeled with a principal (system if the frame is executing code
that is part of the VM or the built-in libraries and untrusted otherwise). Each stack frame also
includes a flag that specifies whether privilege is full. A system class can set this flag, thus enabling
its privilege. This need only be done when something dangerous must occur-something that not
every piece of code should be allowed to do. Untrusted code is not allowed to set the flag.
Whenever a stack frame completes its work, its flag (if it has one) disappears.

Every method about to do something potentially dangerous is forced to submit to a stack


inspection. The stack inspection is used to decide whether the dangerous activity should be
allowed. The stack inspection algorithm searches the frames on the caller's stack in sequence from
the newest to the oldest. If the search encounters an untrusted stack frame (which as we know can
never get a privilege flag) the search terminates, access is forbidden, and an exception is thrown.
The search also terminates if a system stack frame with a privilege flag is encountered. In this case,
access is allowed (see Figure 3.6).

Figure 3.6 Two examples of simple stack inspection.

Each stack is made of frames with three parts: a privilege flag (where full privilege is
denoted by an X), a principal entry (untrusted or system), and a method. In STACK
A, an untrusted applet is attempting to use the url.open() method to access a file in
the browser's cache. The VM makes a decision regarding whether to set the privilege
flag (which it does) by looking at the parameters in the actual method invocation.
Since the file in this case is a cache file, access is allowed. In short, a system-level
method is doing something potentially-dangerous on the behalf of untrusted code. In
STACK B, an untrusted applet is also attempting to use the url.open() method,
however in this case, the file argument is not a browser cache file but a normal file in
the filesystem. Untrusted code is not allowed to do this, so the privilege flag is not set
by the VM and access is denied.

Real Stack Inspection

The simple example of stack inspection just given is only powerful enough to implement black-
and-white trust models. Code is either fully trusted (and granted full permission at the same level as
the application) or untrusted (and allowed no permission to carry out dangerous operations).
However, what we want is the ability to create a shades-of-gray trust model. How can we do that?

It turns out that if we generalize the simple model we get what we need. The first step is to add the
ability to have multiple principals. Then we need to have many more specific permissions than full.
These two capabilities allow us to have a complex system in which different principals can have
different degrees of permission in (and hence, access to) the system.

Research into stack inspection shows that four basic primitives are all that are required to
implement a real stack inspection system. In particular, see Dan Wallach's Ph.D. thesis at Princeton
and the paper Understanding Java Stack Inspection [Wallach and Felten, 1998]. Each of the major
vendors uses different names for these primitives, but they all boil down to the same four essential
operations (all explained more fully in the following discussions):

enablePrivilege()
disablePrivilege()
checkPrivilege()
revertPrivilege()

Some resources such as the file system or network sockets need to be protected from use (and
possible abuse) by untrusted code. These resources are protected by permissions. Before code
(trusted or otherwise) is allowed access to one of these resources, say, R, the system must make
sure to call checkPrivilege(R).
If you recall our discussion of the Security Manager from the previous chapter, you'll remember
that the Java libraries are set up in such a way that dangerous operations must go through a Security
Manager check before they can occur. As we said, the Java API provides all calls necessary to
implement a virtual OS, thus making isolation of all required security checks possible within the
API. When a dangerous call is made to the Java API, the Security Manager is queried by the code
defining the base classes. The checkPrivilege() method is used to help make behind-the-scenes
access control decisions in a very similar fashion. To achieve backwards compatibility, the Security
Manager can be implemented using the four stack inspection primitives.

When code wants to make use of some resource R, it must first call enablePrivilege(R). When this
method is invoked, a check of local policy occurs that determines whether the caller is permitted to
use R. If the use is permitted, the current stack frame is annotated with an enabled-privilege(R)
mark. This allows the code to use the resource normally.

Permission to use the resource does not last forever; if it did, the system would not work. There are
two ways in which the privilege annotation is discarded. One way is for the call to return. In this
case, the annotation is discarded along with the stack frame. The second way is for the code to
make an explicit call to revertPrivilege(R) or disablePrivilege(R). The latter call creates a stack
annotation that can hide an earlier enabled privilege. The former simply removes annotations from
the current stack frame.

All three major Java vendors implement a very similar (and simple) stack inspection algorithm. A
generalization of this algorithm, after Wallach, is shown in Listing 3.1 [Wallach and Felten, 1998].

The algorithm searches stack frames on the caller's stack in order from newest to oldest. If the
search finds a stack frame with the appropriate enabled-privilege annotation, it terminates, allowing
access. If the search finds a stack frame that is forbidden from accessing the target by local policy,
or has explicitly disabled its privileges, the search terminates, forbidding access.

It may seem strange that the vendors take different actions when the search reaches the end of the
stack without meeting any of the conditions (sometimes called falling off the end of the stack).
Netscape denies permission, while both Microsoft and Sun allow permission. This difference has to
do with backward compatibility. The Netscape choice causes legacy code to be treated like an old-
fashioned applet, and confined to the sandbox. The Microsoft/Sun choice allows a signed Java
application to use its privileges even without explicitly marking its stack frames, thus making it
easy to migrate existing applications. Since Netscape did not support applications, they felt no need
to follow the Microsoft/Sun approach and instead chose the more conservative course of denying
permission. For more implementation detail on the three vendors' different code signing schemes,
see Appendix C.

Formalizing Stack Inspection

Members of Princeton's Secure Internet Programming team (in particular, Dan Wallach and
Edward Felten) have created a formal model of Java's stack inspection system in a belief logic
known as ABPL (designed by Abadi, Burrows, Lampson, and Plotkin) [Abadi, et al., 1993]. Using
the model, the Princeton team demonstrates how Java's access control decisions correspond to
proving statements in ABPL. Besides putting Java's stack inspection system on solid theoretical
footing, the work demonstrates a very efficient way to implement stack inspection systems as
pushdown automata using security-passing style. Interested readers should see [Wallach and Felten,
1998], which is available through the Princeton Web site
atcs.princeton.edu/sip/pub/oakland98.html. A more recent paper on how to implement stack
inspection more efficiently is also available on the Princeton site.

CHAPTER SECTION:7

Section 7 -- New Security Mechanisms in Sun's Java 2


Now that we have covered the basic concepts and the underlying mechanisms of Java 2 security,
we can delve into the details of the system. Essential mechanisms include many of the things we
have already discussed: identity, permissions, implies, policy, protection domains, access control,
and privilege. Sources for the information presented here include [Gong, et. al., 1997; Gong and
Schemers, 1998].

This section describes Sun's version of stack inspection. Netscape and Microsoft each have their
own version, but we decided to forgo a lengthy discussion of all three systems. Though the vendors
claim they are very different, we think the three systems are really quite similar. Perhaps one day
they will all converge, making developers' and managers' lives much easier.

Identity

Every piece of code needs a specific identity that serves as a basis for security decisions. In Java 2,
each piece of code has two identity-defining characteristics: origin and signature. These two
characteristics are represented in the class java.security.CodeSource, which allows the use of
wildcard entries to denote "anywhere" for origin and "unsigned" for signature.

Origin boils down to the location the code came from specified as a URL. This is the same sort of
identity used in separation of applets in the JDK 1.0.2 class loading scheme. In fact, Java 2 identity
is really an extension of that idea.

Signature is a bit more complicated. Remember, public/private keys come in pairs. As we know,
code can be digitally signed by a person or organization who vouches for it. The key used to
actually sign the code is the signer's private key. The key used to check the signature for validity is
the signer's public key. So, the public key corresponding to the private key used to sign a piece of
code is the second identity characteristic. (In practice, implementations actually use an alias for the
public key corresponding to the private key used to sign the code.)

Many people say that a signature on code tells you "who wrote the code" or "where the code came
from" (we've been guilty of this faux pas ourselves in days gone by), but this is not true. All a
signature tells you is who signed the code. The author, distributor, and signer of the code may all be
different parties. All you know for sure is that the signer vouches for the code. And since it makes
perfect sense for several people to vouch for the same piece of code, a good signature scheme ought
to allow a piece of code to carry several signatures; then each recipient can decide which of the
signers (if any) should be trusted.

Permissions

Requests to perform a particular operation (most notably a dangerous one) can be encapsulated as a
permission. A policy says which permissions are granted to which principals. The abstract class
java.security.Permission types and parameterizes a set of access permissions granted to classes.
Permissions can be subclassed from this class (and its subclasses). Good practice dictates that a
permission class should belong to the package in which it is used. Java 2 defines access methods
and parameters for many of the resources controlled by the VM.

Permissions include:

java.io.FilePermission for file system access


java.net.SocketPermission for network access
java.lang.PropertyPermission for Java properties
java.lang.RuntimePermission for access to runtime system resources
java.security.NetPermission for authentication
java.awt.AWTPermission for access to graphical resources such as windows

Permissions usually include a target and an action. For file access, a target can be a file or a
directory specified as file, directory, directory/file, directory/*, or directory/-. The * denotes all files
in the specified directory. The - denotes all files under the associated file system subtree (meaning
all by itself, - denotes all files in the entire system). Actions for file access include read, write,
execute, and delete.

An example of a file permission is:


p = new FilePermission("/applets/tmp/scratch", "read");

For network access, a target can be an IP address, hostname, or generalized set of hostnames and a
range of port numbers. The target argument takes the form "hostname:port-range". Actions for
network access include: connect, listen, and accept. An example of a socket permission is:
p = new SocketPermission("bigbrother.rstcorp.com:-1023", "connect")

For getting and setting properties, a target is the property (where * denotes all properties). Actions
are get and set. Runtime system resource targets include createClassLoader, exit, setFactory,
thread, multicast, fileDescriptor.read, fileDescriptor.write, and so on. AWT permission targets
include topLevelWindow, systemClipboard, and eventQueue.

Fully trusted Java applications can add new categories of permissions.

Implies

Each Permission class must include the abstract method implies. The idea is straightforward:
having permission x automatically implies having permission y. We denote this x.implies(y) ==
true in code. A permission x implies another permission y if and only if both the target of x implies
the target of y and the action of x implies the action of y. Consider the permission "read file
/applets/tmp/scratch," which can be written as:
p = new FilePermission("/applets/tmp/scratch", "read");

A permission allowing a read on any file in /applets/tmp; that is, a permission denoted by the pair
(/applets/tmp/*, read) implies our example permission p, but not vice versa. Similarly, a given
socket permission s implies another socket permission t if and only if t covers the same IP address
and port numbers for the same set of actions.

Alert readers might have noticed something funny about the implies method: Each permission class
says which other permissions it implies. This is a bit like Johnny writing himself a note saying he
can drive Dad's car. It seems safer to require Dad's signature on the note. Similarly, it would be
safer if permission for A to imply B had to be granted by B.

Policy

Security policy in Java 2 can be set by a user (which is a bad idea since, as we know, users like
dancing pigs) or a system administrator (which in a Catch-22-like situation is also a bad idea since
system administrators are severely overworked). The policy is represented by a policy object as
instantiated from the class java.security.Policy.

The policy is a mapping from identity (as defined earlier) to a set of access permissions granted to
the code. The policy object is a runtime representation of policy usually set up by the VM at startup
time (much like the Security Manager). An example policy object (in plaintext form) is shown here:

grant CodeBase "https://www.rstcorp.com/users/gem", SignedBy "*" {


permission java.io.FilePermission "read,write", "/applets/tmp/*";
permission java.net.SocketPermission "connect", "*.rstcorp.com";
};
This policy states that any applet that arrives from the Web URL "www.rstcorp.com/users/gem",
whether signed or unsigned, can read and write any file in the directory /applets/tmp/* as well as
make a socket connection to any host in the domain rstcorp.com. Policies are usually made of many
grant clauses.

In practice, policy is set in a plaintext configuration file and is loaded into the VM at startup. In
these policies, a public key (usually a very long string of bits) is signified by an alias. The alias is
the name of a signer represented as a string. For example, a popular alias is the string "self",
meaning your own private key. Primitive mechanisms are included to create and import public keys
and certificates into the Java 2 system. (See Appendix C for the details.)

By default, Sun's VM expects to find a system policy in the file


<java.home>/lib/security/java.policy (where <java.home> is a configurable Java property). This
policy can be extended on a per-user basis. User policy files can be found in a user's home directory
in the file .java.policy. The VM loads the system policy at startup and then loads any relevant user's
policy. If neither policy can be found, a built-in default is used. The built-in default policy
implements the base Java sandbox model.

It is possible to specify a particular policy to use when invoking an application. This is carried out
by using the Java-property-defining -D flag as follows (for the example, our application is the
appletviewer):
appletviewer -Djava.policy=/home/users/gem/policy <applet>

Note that when application policy is defined in this way, neither the system policy nor any user
policy is enforced.

Mapping Policy

Code's identity is checked against the entries of a policy object to determine what permission(s) a
piece of code should be given. At the most basic level of understanding, a match is made when both
the origin and the signature match. In terms of origin, this means the URL defining the origin for a
piece of code is a prefix of a policy entry's CodeBase pair. In terms of signature, this means one
public key corresponding to the signature carried by the code matches the key of a signer in the
policy. Verification of signatures makes use of functionality in the java.security.cert package,
which is a Java implementation of X.509v3 certificates.

Code can be signed with multiple signatures. In case the signatures a piece of code carries have
different policy entries, all entries apply in an additive fashion. That means code is given the union
of all permissions in every match (see Figure 3.7).
Figure 3.7 The danger of additive policy.

Consider the program X shown here. In one case, X is signed by thing1. In


another, code is signed by both thing1 and thing2. In the second case, the policies
of both thing1 and thing2 apply to the code (meaning in this case that it has more
permission to do dangerous activities). A policy administrator may forget to
anticipate what happens when code is signed by multiple keys.

Consider the program X shown here. In one case, X is signed only by thing1. In another, code is
signed by both thing1 and thing2. In the second case, the policies of both thing1 and thing2 apply to
the code (meaning in this case that it has more permission to do dangerous activities). A policy
administrator may forget to anticipate what happens when code is signed by multiple keys.

Protection Domains

Sun says that classes and objects in Java 2 Java belong to protection domains. In fact, protection
domain is just a fancy name for a bunch of classes that should be treated alike because they came
from the same place and were signed by the same people. (The fact that protection domain means
something completely different to people familiar with the security literature is reason enough to
avoid the term.) An object or class belongs to one and only one protection domain. This should ring
a bell, since classes can have one and only one class loader (the one that loaded them). So really
this is a new way of describing a somewhat familiar logical construct for grouping classes together.
A class belongs to the protection domain associated with the class loader that loaded the class.

Permissions are granted to protection domains and not directly to classes and objects, as Figure 3.8
reflects. The class java.security.ProtectionDomain is private in its package and is used internally to
implement protection domains. As we discussed earlier, a domain is made up of a set of objects
belonging to a principal. In Java 2, protection domains are based on identity and can be created "on
demand." The Java runtime maintains a mapping from code to protection domains to permissions
(see Figure 3.8).

Figure 3.8 Grouping classes together to map them to


policy.

Classes map into what Sun calls protection domains which


in turn map to permissions. Policy is defined in terms of
protection domains.
System security policy specifies which protection domains should be created and which protection
domains should be granted what permissions.

There is one protection domain that is special: the system domain. The system domain includes all
system code loaded with the Primordial Class Loader. This includes classes in the CLASSPATH.
The system domain is given special privileges.

Access Control

The java.security.AccessController class implements a stack inspection algorithm similar to the one
we described earlier. Any code is allowed to query this class, which performs a dynamic inspection
of the relevant thread's runtime stack. The method used to implement the check is
checkPermission(), which takes as its argument a Permission object. If the call returns silently,
permission is granted and the potentially dangerous computation can proceed. If the call fails, an
AccessControlException is thrown.

Using the Access Controller


Access control under JDKs previous to Java 2 typically used the Class Loader and Security
Manager to make access control decisions. For example, the following code snippet checks whether
a file /tmp/junk can be read in the old-fashioned way:

ClassLoader loader = this.getClass().getClassLoader();


if (loader != null) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkread("/tmp/junk");
}
}

Here's how to do the same thing in Java 2 fashion (using the Access Controller):

FilePermission p = new FilePermission("/tmp/junk", "read");


AccessController.checkPermission(p);

The Access Controller call performs the appropriate stack inspection.

Privilege

Up through JDK 1.2beta3, Sun's JDK used the primitives beginPrivileged and endPrivileged as
versions of the stack inspection primitives enablePrivilege and disablePrivilege we described in our
discussion of stack inspection. These are the calls that a piece of privileged system code (that is
allowed to do things like perform file access) was supposed to use to grant temporary permission to
less-trusted code. These calls were featured in a number of technical publications from Sun [Gong,
et. al., 1997; Gong and Schemers, 1998].
The idea is to encapsulate potentially dangerous operations that require extra privilege into the
smallest possible self-contained code blocks. The Java libraries make extensive use of these calls
internally, but partially trusted application code written using the Java 2 model will be required to
make use of them, too.

Correct use of the JDK primitives required using a standard try/finally block is as follows:

<normal code>
try {
AccessController.beginPrivileged();
<insert dangerous code here>
} finally {
AccessController.endPrivileged();
}
<more normal code>

This usage was required to address the problem of asynchronous exceptions (though there was still
some possibility of an asynchronous exception being thrown in the finally clause sometime before
the endPrivileged() call).

Wallach and Felten first explained a particularly efficient way to implement stack inspection
algorithms in [Wallach and Felten, 1998]. Unfortunately, Sun decided to abandon the
multiprimitive approach to stack inspection (which could benefit from Princeton's security-passing
style implementation). In fact, JDK 1.2beta4 introduced a completely new API for privileged
blocks. The new API removes the need for a developer to: 1) make sure to use try/finally properly,
and 2) remember to call endPrivileged(). The try/finally usage was symptomatic of a problem that
could only really be fixed with some changes to the VM specification and its resulting
implementations.

In order to properly implement the early API, VMs would have been forced to keep track of the
beginPrivileged() call (unless they adopted a security-passing style approach). This requires
tracking a stack frame (the one where the beginPrivilege is mentioned) and matching the beginning
of a privileged block to its corresponding end-every time a privileged block is used. Doing all this
bookkeeping is inefficient and thwarts optimization tricks that compilers like to play. For example,
just in time (JIT) compilation approaches are hard to adapt to this model. Plus it turns out that
security boundaries are crossed many thousands of times a second, so even a slight delay gets
magnified quickly.

Bookkeeping would slow the VM down, which is about the last thing Java VMs need now as they
near native C speeds. A Sun document explaining the change (from which some of the material
here was drawn) is on the Web at www.javasoft
.com/products/jdk/1.2/docs/guide/security/doprivileged.html.

The new API interface wraps the complete enable-disable cycle in a single interface accessed
through a new AccessController method called doPrivileged(). That means the VM can efficiently
guarantee that privileges are revoked once the method has completed, even in the face of
asynchronous exceptions.

Here's what the new usage looks like. Note the use of Java's new inner classes capability:
somemethod() {
<normal code>
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
<insert dangerous code here>
return null;
}
});
<more normal code>
}

Ironically, one of our developer rules for writing more secure Java code is to avoid using inner
classes (see Chapter 7, "Java Security Guidelines: Developing and Using Java More Securely")!
But if you want to include privileged blocks in your Java 2 code, you are encouraged to use them.
In addition to the inner-class problem, verbosity is also a problem with the new API.

It turns out that using the new API is not always straightforward. That's because anonymous inner
classes require any local variables that are accessed to be final. A small diversion can help explain
why this is.

Closures
The new API is doing its best to simulate what programming languages researchers call closures.
The problem is, Java doesn't have closures. So what are they anyway? And why are they useful?

Functions in most programming language use variables. For example, the function f(x)=x+y adds
the value of the formal parameter x to the value of variable y. The function f has one free variable,
y. That means f may be evaluated (run) in different environments in which the variable y takes on
different values. In one environment, E1, y could be bound to 2. In another environment, E2, y
could be bound to 40. If we evaluate f(2) in E1, we get the answer 4. If we evaluate f(2) in E2, we
get the answer 42.

Sometimes we want a function to retain certain bindings that its free variables had when it was
created. That way we can always get the same answer from the expression. In terms of our
example, we need to make sure y always takes on a certain value. What we want is a closed
package that can be used independent of the environment in which it is eventually used. That is
what a closure is. In order to be self-contained, a closure must contain a function body, a list of
variables, and the bindings of its variables. A closure for our second example might look like this:
[{y=40;} f(2)=2+y]

Closure is particularly useful in languages with first-class functions (like Scheme and ML). In these
and other related languages, functions can be passed to and returned from other functions, as well
as stored in data structures. Closure makes it possible to evaluate a function in a location and
external environment that may differ from where it was created. For more on this issue, see
[Friedman, et al., 1992; Fellisen and Friedman, 1998].

As we said before, Java does not have closures. Java's anonymous inner classes come as close to
being closures as Java gets. A real closure might include bindings for the variables that can be
evaluated sometime in the future. In an anonymous inner class, however, all state must be made
final (frozen) before it is passed in. That is, the final state is the only visible state inside the inner
class. This is one reason anonymous inner classes are not true closures. The problem of making
everything final turns out to have strong implications for the use of the new privileged block API.

Local Variables
For example, in the following code, the variable lib must be declared final if it is to be used inside
the privileged block:

randommethod() {
<normal code>
final String lib = "awt";
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
System.loadLibrary(lib);
return null;
}
});
<more normal code>
}

Making all local variables that are to be accessed in the block final is a pain, especially if an
existing variable can't be made final. In the latter case, the trick is to create a new final variable and
set it to the non-final variable just before the call to doPrivileged. We predict this will be a source
of both headaches and errors. Errors may lead to security problems.

What Comes Out


Another problematic issue with the new interface is the fact that the inner class always returns an
Object (a return type seen throughout the Java language). That means if a call to a piece of
privileged code (for example, a call to System.getProperty()) usually returns something other than
an Object (for example a String), it will have to be dynamically cast to the usual type. Using a final
variable to pass types out is possible, too. Unfortunately, both of these operations will incur a
runtime performance hit (especially casting). The returns-only-Object problem is another source of
potential errors.

Whence the Change


It is good that VM vendors want their machines to be fast and efficient; however, purely in terms of
security, it is unclear whether the decision to change the API was a good one. Not that the previous
API was perfect, but the new one seems to introduce several places in which errors are bound to be
made by developers charged with actually using VMs. The real answer to the problem is
introducing closures to Java. Closures are something to look for in future JDK versions.

The Security Manager Revisited


As we described in Chapter 2, "The Base Java Security Model: The Original Applet Sandbox," the
Security Manager up until JDK 1.1 invoked a direct check() method for dangerous resource access
control. This method was responsible for evaluating the request and denying or granting access.
The new Security Manager in Java 2 still supports the use of check() methods, but now many of
these calls are actually implemented to make use of the Access Controller and Permission objects
(whenever possible).

It would be best to dispense entirely with the Security Manager, but history dictates that it remain
available for reasons of backwards compatibility. Breaking all existing JDK 1.1 code in order to
introduce a new security design is not an economically viable approach for Java.

The Secure Class Loader

Java 2 introduces the class java.security.SecureClassLoader, which is a concrete implementation of


the abstract ClassLoader class. It tracks the code source and signatures of each class, and hence
assigns classes to protection domains. All Java code is loaded by a Secure Class Loader (except for
code loaded by the Primordial Class Loader) either directly or indirectly (that is, by another class
loader that was itself loaded by the Secure Class Loader). For more on class loading, refer
to Chapter 2.

Sandboxing Java Applications

Now that the security enforcement mechanisms are more complex and do not rely on the distinction
between applet code and built-in code (as in the early days), it is possible (and desirable!) to force
Java applications, in addition to applets, to run within the (highly mutable) sandbox. This means
application code can be made to cohere with locally defined security policy. Java 2 provides a
mechanism for doing this with the class java.security.Main. The implementation ensures that local
applications stored in the java.app.class.path are loaded with the Secure Class Loader. It is a good
idea to have applications run from this location as opposed to placing them in the CLASSPATH
where they will be treated as built-in code.

Adding Permissions

It is possible to add new permissions to Java that are tailored to your specific needs. This is done by
subclassing and extending the java.security.Permission class we detailed earlier. The new
permission classes that you create should be stored in the application package where they apply.

Next, a representation of the permission (that is, a string representing a policy entry) needs to be
added to the policy file. This ensures that the permission is "automatically" configured for each
domain.
Finally, the application code itself may include a section that manages resources. This section of
code should make use of the checkPermission() method of the AccessController class (explained
earlier). Use of this method obviates the need to think about Class Loaders and Security Managers.

CHAPTER SECTION:8

Section 8 -- Outside the Sandbox


Java 2 clearly introduces significant changes to the Java security landscape. It is likely that the days
of black-and-white security policy for mobile code are numbered. With the major changes to Java's
security architecture come a number of important responsibilities, the most important of which is
mobile code policy creation and management. The tools are still primitive, but the policy itself is
essential.

Also essential to any mobile code system that makes use of code signing is solid key management
capability. Although the subject of public key infrastructure (PKI) is really beyond the scope of this
book, we at least invoke some important concepts. Managers responsible for setting and
maintaining policies based on signed code will encounter issues including choice of certificate
authority, who to issue keys to, how to ensure that private keys are kept private, whether to get a
corporate key and how to protect it, how to disable keys of employees who leave an organization,
where to store keys, and so on. These are nontrivial issues that have yet to be worked out in the real
world.

Hopefully, widespread support for code-signing systems will quickly appear on consumer desktops
worldwide. Truthfully, the PKI is much less mature than many security researchers and pundits
predicted it would be by now. This is partly because deploying an effective PKI is much more
difficult than it sounds. But it is also at least partially due to the greed of certificate authorities who
chose to charge developers for identities (public/private key pairs) instead of issuing them for free
and charging elsewhere for their use. Without a solid PKI, systems like Java 2 Java may take a
while to catch on. We predict that signed mobile code will find its most pervasive use among early
adopters as an intranet technology (as opposed to an Internet technology). Of course, we're very
well prepared to be wrong about that.

For a long time, Java developers have wanted some way in which less restriction could be placed
on their applets. At the same time, managers in many enterprises have been searching for ways to
manage code (not just mobile code, but any code) more securely. In its Java 2 guise, Java offers a
powerful answer to these needs.

We would be irresponsible not to note that with code signing comes a host of new risks to manage.
Most notable among the risks are two: first, that the implementation will have holes (JDK 1.1 code
signing has already fallen prey to this risk); and second, that security policies will get too
complicated to understand and manage.
Malicious Applets:
Avoiding a Common Nuisance
Chapter 2, "The Base Java Security Model: The Original Applet Sandbox," and Chapter 3, "Beyond
the Sandbox: Signed Code and Java 2," explain how Java 2's security system works. This chapter
and the next explain how it doesn't. Unfortunately, it is entirely possible to (mis)use Java,
especially in its applet form, as a vehicle for attacking systems. Language-based security controls
like those found in Java make writing a hostile applet more difficult than it might be otherwise, but
they don't make it impossible. (Recall that Java security stacks up favorably against competing
mobile code systems like ActiveX, as we discussed in Chapter 1, "Mobile Code and Security: Why
Java Security Is Important.") Applets that misbehave and do something that their users don't want
to happen are called hostile applets.

There are two varieties of hostile applets: malicious applets and attack applets. The names of the
two classes make it clear which is the more serious variety. Fortunately, attack applets are not
commonly encountered on the Web; in fact, no attack applets have been seen to date in the wild
(that is, outside the labs in which they were created). That's not to say that attack applets are not
real. They are. Attack applets are real applets, written in everyday Java, that work against popular
browsers such as the one you use. Attack applets have been created and extensively tested in the
laboratory. (We return to the subject of attack applets in Chapter 5, "Attack Applets: Exploiting
Holes in the Security Model.") There is, however, another more pervasive kind of hostile applet,
not as serious a security concern, but still worthy of attention-the malicious applet.

Unlike their attack applet cousins, malicious applets have escaped the lab. Such realities make it
necessary for all users of Java-enabled browsers (and their trusty system administrators) to be
aware of Java security threats. Simply surfing over to a Web page containing a hostile applet allows
it to invade your machine with its malicious code. This chapter explores many malicious applets,
ranging from the merely annoying to the more seriously disturbing.

Near the beginning of Chapter 2, classes of potential Java threats were discussed. The four classes
of attacks named were system modification attacks, invasion of privacy attacks, denial of service
attacks, and antagonistic attacks. Java is a powerful enough language that, without security
constraints placed on applets, it is possible to implement all four such classes of attacks. The Java
security model was designed to thwart those threats perceived to be the greatest dangers.

Much ado has been made over Java security problems, and there have in fact been a number of
serious flaws. We detail the truly serious problems in Chapter 5. Such problems result in intrusions
that allow arbitrary system modification (effectively, unlimited access). An attack applet based on
one of these strategies constitutes a cracker breaking into your machine.

It is true that the very serious attacks of the next chapter require an in-depth understanding of both
Java and the Internet. It has been argued that we should feel fairly confident that few people will be
able to exploit such esoteric vulnerabilities. That position is a dangerous one to take. One instance
of a cracker discovering a novel attack applet will change such statements considerably. Once
loose, attack applet information would quickly spread throughout the cracker community. Our job
as security researchers is to find security holes and plug them before they are used by dishonest
people. Security researchers also work to create such a secure model that holes are very rare.
Fortunately, none of the serious attacks have shown up in the form of attack applets, although the
possibility looms ominously.

Don't breathe a sigh of relief yet. Tampering with Java security does not always require wizardry.
In fact, writing Java code to breach security can be easy. This chapter discusses some simple Java
applets gone bad. Such applets are known on the Net as malicious applets. Entire collections are
available for anyone interested to see, to adapt, and to use. See, for example:

• The Hostile Applets Home Page at www.rstcorp.com/hostile-applets


• DigiCrime at www.digicrime.com
• The Java Security Hotlist: Hostile Applets and Other Toys
at www.rstcorp.com/javasecurity/applets.html

The best first defense against these sorts of applets is to learn about them.

Chapter Four Sections


1. What Is a Malicious Applet?
2. Annoying Applets
3. Denial of Service
4. Opening Untrusted Windows
5. Stealing Cycles
6. Forging Mail
7. Killing Off the Competition
8. Malicious Applets on the Web
9. The Implications

Malicious Applets: Avoiding a Common Nuisance


CHAPTER SECTION:1

Section 1 -- What Is a Malicious Applet?


A malicious applet is any applet that attacks the local system of a Web surfer using one of the three
less-serious classes of attacks discussed inChapter 2. Malicious applets involve denial of service,
invasion of privacy, and/or annoyance. Malicious applets are written by researchers, crackers, and
Net miscreants to harass, annoy, and damage Java users. They can even seriously damage a Java
user's machine. Any applet that performs an action against the will of the user who invoked it
should be considered malicious.

It is important to emphasize again that use of the term Java user applies equally to Java developers
and people surfing the Web with a Java-enabled browser. Using Java does not require any
programming, or even possession of the JDK; it is enough to use a Java-enabled browser. Under
this definition, most people who surf the Web with Java on are Java users. Malicious applets exist
on the Web today that do the following bad things:

• Forge mail from you to whomever the evil applet's author chooses, saying whatever they
wish while masquerading as you
• Steal your CPU cycles to perform their own work while your legitimate processes languish
• Crash your local system by using all available system resources

These activities are both impressive and daunting, and we have only scratched the surface.

There are also malicious applets created simply to annoy. These applets go only a bit too far,
lingering at the edge of respectability. These sorts of applets do things like play sound files
continuously, set up threads that monitor your Web use, and display unwanted graphics on your
screen.

Stopping Malicious Applets before They Start

What can be done to stop malicious applets from doing their evil deeds? The best alternative now is
to set a security policy that allows only applets signed by trusted parties to run. But if you want to
surf with a Java-enabled browser and run every Java applet you come across on the Web, the safest
thing to do is to avoid unknown and untrusted Web sites unless you first disable Java. Just by using
a Java-enabled browser to surf the Web, you are open to attack by both attack applets and malicious
applets. This danger, combined with the serious attacks discussed in Chapter 5, has caused the
CERT Coordination Center to recommend disabling Java when surfing untrusted sites [CERT,
1996a; CERT, 1996b].

What can be done to stop these applets from doing their evil deeds in the future? There are many
possibilities. One interesting approach would be to write detectors for bad applets based on known
vulnerabilities. That way, they could be screened out by the byte code Verifier (or some similar
extension). Princeton's Secure Internet Programming team has investigated this possibility
extensively, and research at Reliable Software Technologies continues. (It turns out that the
problem is harder than it may seem on first consideration.) A number of commercial enterprises
now sell products that claim to screen byte code for malicious characteristics. We investigate these
products in Chapter 6, "Securing Java: Improvements, Solutions, and Snake Oil."

Another way to protect against malicious applets is by improving Java's security model. To the
extent that any holes identified by researchers have been quickly and thoroughly patched, the
security model can be said to be improving. However, the practice of patching software after
exploits have been demonstrated is backwards. This unfortunately common strategy is known as
penetrate and patch, and has been criticized for many years by security practitioners. (For more on
this issue, see [McGraw, 1998].) Better software engineering and more thorough software
assurance practices are much more appealing. It is even possible to write your own code
defensively so it makes a much harder target for bad guys to attack (see both the guidelines
of Chapter 7, "Java Security Guidelines: Developing and Using Java More Securely," and Chapter
9, "The Future of Java Security: Challenges Facing Mobile Code").

The addition of code signing to Java in JDK 1.1 and its extension with access control in Java 2
allow for the creation of complex security policies based on authentication of applet signers. Using
this technology, a Web surfer could specify a list of trusted sites whose applets should be allowed
to run without restrictions. The trick is creating a sound security policy and correctly coding it into
your browser.

The next few sections discuss various kinds of malicious applets. Starting with the least worrisome
category-the merely annoying-the text progresses through the truly malicious machine-hangers.
Possible motives for creating these applets are discussed along the way. Keep in mind while you
read this chapter that the malicious applets described here pale in comparison with the attack
applets described in Chapter 5. Fortunately, the security researchers who discovered those
vulnerabilities are the good guys.

Disabling Java

Certainly, many unsavory characters are on the Net, and many of them have created similarly
unsavory Web pages. If for some reason you wish to check such sites out, it would be a good idea
to disable Java first.

The best way to protect yourself from malicious applets is to create a policy that allows only code
you trust to run. It is quicker and easier, however, to disable Java when surfing dangerous Web
sites. Much like being street-wise in a big city, your choice to use or disable Java depends on what
browsing you will be doing. If you keep to the sites of big business, you are less likely to find
dangerous applets, just as the finance districts of New York and Chicago are less dangerous than
the housing projects. Know where you are on the Web and take precautions accordingly.

Disabling Java in Netscape Navigator 4.x


Disabling Java under Netscape Navigator 4.x is easy and can be done in the midst of a session.
Reenabling Java is also possible, should you change your mind. Figures 4.1 and 4.2 explain how
Java can be disabled in Netscape Navigator 4.x.
Figure 4.1 Disabling Java in Netscape Navigator under Solaris.

From the Edit menu, select Preferences. Click on the word Advanced, which will
appear in the window entitled "Category" (as shown here). To the right of that
window, a series of radio buttons will appear, one of them labeled Enable Java. Click
the button so it appears to be sticking out. Finally, click OK, and Java will be
disabled.

Figure 4.2 Disabling Java in Netscape Navigator under Windows NT.

From the Edit menu, select Preferences. Click on the word Advanced, which will
appear in the window entitled "Category" (as shown here). To the right of that
window, a series of checkboxes will appear, one of them labeled Enable Java. Click
on the check box, removing the check mark. Finally, click OK, and Java will be
disabled.

Disabling Java in Internet Explorer 4.x


Internet Explorer also allows Java to be disabled. Figure 4.3 explains how Java can be disabled in
Internet Explorer 4.x.
Figure 4.3 Disabling Java in Microsoft Internet Explorer.

Unfortunately, under MSIE you can only disable Java on a per-zone basis. From
the View menu, select Internet Options. Click on the Security tab. Select the
zone for which you want to disable Java (for example, the Internet zone). (For
more on zones, see Chapter 1.) Then select the Custom radio button and click
on the Settings button. A dialog will come up called Security Settings. This is the
dialog shown here. Scroll down until you see the Java category. It will have an
expanded sub-category called Java permissions. The sub-category will have a
list of options. Click on the circle labeled Disable Java, then click OK. You'll then
be back at the Security tab. Click OK again, and Java will be disabled.

Snagging Malicious Code

Running malicious code, especially code that explicitly claims to be malicious, is always an
interesting conundrum. You may want to check a malicious applet out to see how it works against
your machine, but at the same time, you don't want your machine to be compromised by an attack.
Although it is advisable to avoid running malicious code at all, some people can't resist. The
following strategy may help.

With Java off, surf to the Web page with a suspected malicious applet; for example, the DigiCrime
site includes a malicious applet called "bluescreen" at www.digicrime.com/exploits/javawin. A
quick look at the HTML source for the page shows that the malicious applet is invoked with the
following code snippet:
<APPLET CODE="bluescreen.class"
CODEBASE="http://www.digicrime.com/surprise" width=1 height=1>

This gives you all the information you need to snag the class file (without running it) and bring it
home for dissection.
The next step is to point your browser directly at the class file by opening
URL www.digicrime.com/surprise/bluescreen.class. Your browser will display some gobbledygook
like Ê_°_me`, which is actually the byte code of the class file as shown in "human readable" form.
Use the Save As feature of your browser to save the file bluescreen.class on your disk.

Now that you have the file, you can use a Java decompiler to turn it back into readable Java source
code. The once-popular Mocha decompiler is now out of date as its author, Han Peter van Vliet,
died an untimely death. However, there are other decompilers available. The best is the
SourceAgain decompiler from Ahpah Software. After decompiling the byte code, you can inspect
the resulting Java source code for malicious behavior.

Note that the decompilation issue is a complex political football. Laws have been proposed that
make decompiling copyrighted code illegal, even if it is done by security researchers for the
purposes of code analysis. The intent of the laws is to prevent software piracy, but they are overly
broad in their language. The bottom line is to educate yourself about such laws before you begin
decompiling code to analyze it.

Once you know what a malicious applet is going to do, you may feel more comfortable running it.
Keep in mind that there is no guarantee that the class file that you grab and decompile will be the
same as the one you eventually run from a Web site. The Web server can easily be programmed to
bait and switch so that it changes the class file that it distributes unpredictably. Unless you run your
grabbed copy of the class file from your own Web server, all bets are off.

CHAPTER SECTION:2

Section 2 -- Annoying Applets


The simplest kind of malicious applet is only annoying. Malicious applets of this type do things just
beyond acceptable. Because Java has powerful multimedia packages, annoying applets can do a
large variety of things, ranging from playing sound files continuously to displaying obscene
pictures.

Java has attracted its share of bad programmers and, depending on your point of view, bad Java
code can be annoying. To be counted as a hostile applet, some malicious intent on the part of the
author is usually required; therefore, unintentionally lousy Java code may not count. Just for the
record, a poorly written Java applet may aid a real cracker in breaking your Java security system.
Avoid running crummy code, and if you're developing code, use sound software engineering
practices and follow the guidelines provided in Chapter 7.
One particularly humorous annoying applet opens a dialog box with the message "April Fools" and
an OK button. Users naturally think the applet is supposed to exit when you click OK as it says on
the screen. But the dialog box zips around the screen, avoiding the mouse as the user tries to click
OK. This quirk renders the applet difficult to stop through normal means. The easiest way to get rid
of it is to exit the browser.

Listing 4.1 provides the code for another annoying applet based on an idea from Mark
LaDue.1 This applet appears to be well-behaved, at first. All it does is display a nice little picture of
one of the author's dogs (and baby Jack). It also plays some appropriate background sound (the dog
barking). Not all that exciting for an applet. The code is shown in Listing 4.1.

Listing 4.1 NoisyApplet sustains a thread past the time its creating class is unloaded.

/* NoisyApplet.java */
/* Adapted from the NoisyBear applet of Mark LaDue. */
/* You will need a sound file and a picture to make */
/* this work. */
/* This applet is provided solely as an example and */
/* is not guaranteed to do anything. */
/* Use it at your own risk. */

import java.applet.AudioClip;
import java.awt.*;

public class NoisyApplet extends java.applet.Applet implements Runnable {


Font msgFont = new Font("TimesRoman", Font.PLAIN, 36);
Thread noisethread = null; // thread to run sound in
Image jackImage;
Image offscreenImage;
Graphics offscreenGraphics;
AudioClip bark; // sound file variable

// the init() runs to set things up


public void init() {
// first, set up the picture
jackImage = getImage(getCodeBase(), "jack+waldog.jpg");
offscreenImage = createImage(this.size().width,
this.size().height);
offscreenGraphics = offscreenImage.getGraphics();
// then load the audio file
bark = getAudioClip(getCodeBase(), "bark.au");
}

// the start() method runs whenever you enter the


// applet's page. it also runs after init()
public void start() {
// start a thread to run the audio clip in
if (noisethread == null) {
noisethread = new Thread(this);
noisethread.start();
}
}

// the stop() method runs when you exit


// the applet's page
public void stop() {
if (noisethread != null) {
// uncommenting the following 2 lines will stop
// the sound
// if (bark != null)
// bark.stop();
noisethread.stop();
noisethread = null;
}
}

// this starts the ball rolling by telling the sound


// to "go"
public void run() {
if (bark != null) bark.loop();
}

// the paint() method draws the graphics


public void paint(Graphics g) {
int jack_width = jackImage.getWidth(this);
int jack_height = jackImage.getHeight(this);

offscreenGraphics.drawImage(jackImage, 0, 0,
jack_width, jack_height, this);

offscreenGraphics.setColor(Color.red);
offscreenGraphics.setFont(msgFont);
offscreenGraphics.drawString(
"Walnut says HI HI HI ...", 150, 225);
// actually draw the image
g.drawImage(offscreenImage, 0, 0, this);
}
}

This applet has been tested and proven to be annoying on a large number of platform/browser
combinations.

What makes the NoisyApplet annoying is that the sound never stops, even if the user surfs to
another Web page. How could this happen? Simple; the applet starts a thread that never stops!

Listing 4.2 Spinning a thread forever.

public void stop() {


if (noisethread != null) {
// uncommenting the following 2 lines will
// stop the sound
// if (bark != null)
// bark.stop();
noisethread.stop();
noisethread = null;
}
}

Why does the sound keep on going? As you can see in the Listing 4.2, the stop() method of the
NoisyApplet class has been redefined. The redefinition makes the thread live until the user either
figures out how to kill the thread, disable audio on the target machine, or quit the browser.2None of
these options are particularly convenient.

The lines commented out would silence our NoisyApplet by installing a more normal stop()
method. By commenting out these lines you can turn our somewhat-typical applet into a malicious
annoyance. Clearly, the line between an honest mistake and an antagonistic programming practice
is very fine indeed.

There are some interesting implications that our simple annoying applet introduces. Any thread can
employ the same strategy of redefining the stop() method in order to run ad infinitum. Depending
on the way a Security Manager is written, Java may not require a programmer to stop all threads.
Overriding the default stop() method is, as we have shown, a trivial exercise. Threads can run even
in the absence of the applet that spawned them, meaning that it is possible to write threads that
monitor what the user is doing.

Going beyond stop()

Redefining the stop() method is an easy thing to do, but it is sometimes possible to determine that
an applet is doing this by inspecting its byte code. An alternative to redefining the stop() method is
wrapping an attack in a try/finally block. This technique is often seen in malicious applets. In fact,
today's slickest malicious applets both redefine stop() and use a try/finally strategy. An example is
shown in Listing 4.3.

Listing 4.3 Using try/finally to defeat ThreadDeath.

public void run() {


try { //do somthing interesting here
}
catch(ThreadDeath td){System.out.println("Can't touch this!");}
// Return from the dead
finally{
Thread.reborn = new Thread(, "new");
reborn.start();
}
}

The idea is to catch any exceptions that might be sent to an applet thread to try to kill it (for
example, a ThreadDeath exception, which is what is thrown when Thread.stop() is called). These
exceptions are caught in the body of the block. In the finally block, a malicious applet can include
code to resurrect itself in case of a problem.

Note that the same sort of thing can be done using an applet's finalize() method, which gets invoked
during garbage collection.

This technique counters all attempts to kill a thread and can be used to defeat a majority of
commercial Java security packages that claim to be able to kill hostile applets. Stopping an applet
that does not want to be stopped is a nontrivial undertaking. One approach might be to hook deeply
into the Java runtime and terminate an actual OS thread, but this is problematic since it would
sometimes destabilize the entire browser.

Since stopping applets is difficult, making monitoring applets is easy.


The Business Assassin Applet

One such monitoring applet (possibly an armchair exercise) is called the Business Assassin applet
[Dean, et al., 1996; LaDue, 1996]. The Business Assassin targets the applets of a particular Web
site, such as applets from Gamelan.

If you place this applet on your homepage, it will start up threads on the remote machine of anyone
who surfs your site with Java enabled. These threads silently watch for other applets being loaded
from Gamelan. If the monitoring threads detect the user surfing the Gamelan Web site, they begin
the attack. The malicious threads make useless any applets coming in from Gamelan. They kill the
threads of Gamelan applets (something discussed in more detail on page 135). Another feature of
the Business Assassin applet (code disabled by default) goes on to launch a denial-of-service attack
against anyone who visits Gamelan after running the Assassin.

On the surface, the Business Assassin applet appears to be harmless; that is because it uses threads
to do all the dirty work. Threads are not required to stop running when an applet's Web page is
exited. This means that threads can keep running in the browser after an applet has appeared to
finish. In order to have the blame pinned on some other applet, hostile threads can be programmed
to delay their attack until some future time. In the case of the Assassin's threads, an apparent
problem in the Security Manager (only addressed recently by Java 2) allows the Assassin threads to
attack thread groups that belong to applets other than their own. It waits for the target threads from
Gamelan to appear and only then initiates hostile activity.

Applets like the Business Assassin will certainly have a chilling effect on Web-based commerce.
Even if applets that use never-ending threads are not used for annoying things like these, they still
have the potential to be used for information gathering. If an applet can spawn a monitoring thread,
there is no reason that it could not report information it finds interesting back to its server. Such
information could include lists of sites a user has visited, files that he or she has downloaded, the
names of other competing applets run, or a host of other things. Such monitoring applets should be
named BigBrother.

CHAPTER SECTION:3

Section 3 -- Denial of Service


In a cracker's world, the next-best thing to breaking in to your computer is locking you out of it.
After all, if the cracker cannot use your computer, then neither should you! Attacks that prevent
someone from using his or her machine are called denial-of-service attacks in the security
community. Denial of service comes in many guises. Attacks may involve consuming all available
CPU cycles, allocating every last bit of memory, hogging all possible screen space, or causing a
system to hang while it waits for something impossible to happen. The one common requirement is
that the user being attacked is effectively locked out of his or her machine. An effective denial-of-
service attack happens so quickly that it's usually impossible to stop it. Experiencing such an attack
is a sobering event.

There are many ways to write applets initiating denial-of-service attacks. We think these attacks are
serious enough that code for them is not listed here. Examining some attacks through high-level
description and occasional code fragments should be enough to illustrate a point. Realistically, the
sorts of malicious applets being described are not too difficult to dream up. People who are
interested will be able to either create the code or find it, as they see fit.

Consider the denial-of-service attack presented in Listing 4.4. It recycles an idea from the
NoisyApplet making use of nonterminating threads. Quite simple to formulate, it looks something
like this:

1. Create an applet that starts a thread with its priority set to MAX_PRIORITY. This makes the
thread run as quickly as possible and gives it a leg up in the ongoing competition between
threads for CPU time.
2. Redefine the stop() method to null for the thread.
3. Do something silly in the main part of the applet so that it appears to be harmless. Show a
picture or display some cute animation.
4. Have the thread sleep for a while to delay its malicious activities. Such sleight of hand will
have the effect of placing the blame somewhere else when the thread wakes back up to
perform its dirty work.
5. When the thread wakes up, have it begin calculating in an infinite loop (or some other CPU-
intensive activity that eats cycles). Such a computation will, in effect, bring the browser
down by taking away all available computational resources. One particularly worthy
function for intense calculation overload is the Ackerman function shown in Listing 4.4.
Listing 4.4 The Ackerman function implemented as a Java application.
Ackerman takes integer n and exponentiates n by itself n times. This means that Ackerman(3) is equivalent to three cubed, cubed. The program can be run by
typing the command java Ackerman n, where n is an integer. Computing anything greater than Ackerman(3) takes a long time and many CPU cycles. This code
was written to be as inefficient as possible. This version of Ackerman could easily be used in a denial-of-service attack.
import java.lang.System;
import java.io.IOException;
import java.lang.Math;

class Ackerman {
public static void main(String[] args) {
long ackValue = 1;
long exp = 0;

if (args.length >= 1) {
try { exp = Integer.parseInt(args[0]); }
catch (NumberFormatException e) { exp = 0; }
}

if (exp > 1) {
ackValue = exp;
int numLoops = (int)exp;
for (int i = 1; i < numLoops; i++) {
exp = ackValue;
for (int j = 1; j < numLoops; j++) {
ackValue = ackValue * exp;
System.out.println("current value is " + ackValue);
}
}
System.out.println("Ackerman vlue: " + ackValue);
}
}

This denial-of-service approach is simple and elegant. There are literally hundreds of things that
can be done in Step 5. Other possibilities include endlessly appending to a StringBuffer and using
drawString to display its entire contents. This ends up as a double whammy, eating both CPU
cycles and memory. Another possibility would be calculating p using the most inefficient algorithm
possible. If you have trouble remembering how to code poorly, just teach an introductory
programming course to jog your memory.

On a more serious note, this line of attack is both very simple and very successful. Most browsers
seize up and die under such an attack.

Applets that implement these strategies exist on the Web now. Skeptical readers are welcome to
surf over and kill their browsers personally (www.rstcorp.com/hostile-applets/). Just surf to a Web
page containing hostile applets using a Java-enabled browser, and they will automatically be
invoked. Short of creating and enforcing a mobile code policy based on code-signing, nothing can
be done to prevent them. Chapter 6 examines some strategies for protection, but they are all still in
early development. Just to complicate matters, such a hostile applet can implicate other Web pages
by using delay tactics discussed previously.

Is Denial of Service All That Bad?

There is no doubt that denial-of-service attacks are less serious than security breaches of the sort we
discuss in Chapter 5. Although a browser might be crashed with such attacks, intruders do not gain
entry into a system. This has led some people at JavaSoft to dismiss this class of attacks as
unimportant. Before Arthur van Hoff (one of the original designers of Java) left JavaSoft for
Marimba, he posted a note to comp.lang.java that dismissed such problems as either not very
serious or a concern for browser vendors-not JavaSoft. Although the most serious security
problems should be addressed first, denial-of-service applets should also be addressed. Using
resource allocation limitations-for example, placing upper limits on CPU usage, number of
instructions that can run, or number of windows allowed-is one line of defense. Threads should not
be able to override the stop() method so easily, either. (The ability to override stop, or not, is now
enforced by policy.)

It is ironic that some of the most Java-heavy Web pages almost go as far as denial of service in
doing what their programmers intended. Some Java-enhanced sites take quite a while to load (we
have heard of some applets that take 10 minutes to start up, even over a very fast T1 connection).
The bottleneck likely involves the byte code Verification process and not the network transmission
time. Of course, slow loading/verifying really doesn't constitute a true denial-of-service attack.

Even a denial-of-service attack that is no big deal when embedded in an applet becomes a serious
problem if it can be launched against a Web server. Sites that use Java-enabled Web servers and
configure those servers to allow anyone to upload a servlet are exposing themselves to easy denial-
of-service attacks. In short, anyone, anywhere in the world, can bring down such a server whenever
he or she feels like it. That's why we recommend that Java-enabled servers only accept servlets
from clients that they really trust.

CHAPTER SECTION:4

Section 4 -- Opening Untrusted Windows


A more serious denial-of-service attack than browser-killers involves opening large numbers of
very large windows. There are a couple of reasons why this kind of attack should be considered
more severe. The side effects of this attack tend to freeze access to the keyboard and mouse while
the applet runs. This makes the applet harder to control. Also, the way these windows are created
and mapped makes it possible to pop up untrusted Java applet windows without the mandatory
warning they are supposedly required to display.

A denial-of-service applet based on this idea would be very similar to the ones we discussed
on page 128, with the addition of the window-popping code shown here:

// In the code below, littleWindow is of type Frame


// Adapted from an idea by Mark LaDue
try {
// create a window
littleWindow = new bigFrame("Whocares");
// make it very big
littleWindow.resize(1000000, 1000000);
// position it to cover everything
littleWindow.move(-1000, -1000);
// finally, open the window
littleWindow.show();
}
catch (OutOfMemoryError o) {
repaint();
}
class bigFrame extends Frame { // constructor method
Label 1;
bigFrame(String title) {
super(title);
setLayout(new GridLayout(1, 1));
Canvas whiteCanvas = new Canvas();
whiteCanvas.setBackground(Color.white);
add(whiteCanvas);
}
}

This code opens a very large (1-million x 1-million pixel) white window without the supposedly
mandatory untrusted Java applet window message. Put this code in a loop so many windows pile on
top of each other, and voilá, an applet that consumes major resources in an interesting new way.
The act of generating many windows all at the same time causes many window events to fill the
window manager's event queue. This effectively disables the mouse and keyboard, since they talk
to the machine through window events themselves. The console of the workstation displaying these
very large windows freezes up. There are two things users can do when an attack like this is leveled
against them: Go to another machine on the same network to kill the offending browser processes,
or reboot (usually with the three-fingered salute).

The ability to open a window without the mandatory untrusted window banner is interesting in its
own right. Using variants of such code, it is possible to spoof Web site password panels. This leads
to interesting social engineering attacks, wherein an unsuspecting user is asked to provide his or her
password due to a spurious security alert event. Many users fall for such schemes. After collecting
login and password information, a malicious applet can mail off the information to a collection site
for later use by a cracker.

Spoofing Web site password panels is an interesting illustration of how an attacker can whip up a
serious attack out of a set of holes that each look fairly innocuous. An adversary could create an
applet that appears to stop when the user leaves its page, but really keeps a thread lurking in the
browser. That thread could use monitoring techniques to determine when the user has visited a
particular target site. It could then display a spoof of the target site's log-in panel. The user would
probably be fooled, since the bogus log-in panel would appear at the "right" time.

Applets that use a social engineering attack to collect possibly sensitive information can be found at
the DigiCrime Web site. Surf there at your own risk.

CHAPTER SECTION:5

Section 5 -- Stealing Cycles


Theoretical computer science teaches that some computational problems are much more difficult
than others. Very hard problems scale exponentially. Other problems are solvable in Polynomial
time, but only using an oracle that can correctly decide which path to follow each time a choice is
encountered. The second set of problems is termed NP. One of the most perplexing problems in
computer science was introduced in 1977 by three cryptography researchers named Rivest, Shamir,
and Adelman. They invented a sophisticated encryption algorithm called RSA, (after their initials).
The only known Achilles Heel of the RSA cryptosystem rests on the ability (or rather, inability) to
factor a very large integer into a product of prime numbers in a reasonable length of time. The
exact complexity of prime factoring is not known, but it is expected to be difficult and has proven
to be so thus far.

One particular instance of the RSA problem involves factoring a specific 129-digit number into its
prime factors. Using theoretical computer science as a guide, Rivest, Shamir, and Adelman
estimated that it would take 4 x 1016 years to factor RSA-129. However, applying the quadratic
sieve algorithm along with the collaboration of thousands of volunteers (who donated CPU time on
their workstations), researchers solved RSA-129 in 1994 after less than a year of work. The key to
the solution was using thousands of computers at the same time to attack the factoring problem. To
prove that they had discovered the proper solution, the distributed-factoring researchers used their
solution to break a secret coded message that Rivest, Shamir, and Adelman had created in 1977 as a
test. The message read, "The magic words are squeamish ossifrage."

Java offers a unique opportunity for use in cooperative projects such as factoring RSA-129. Some
of the researchers involved in factoring RSA-129 recently announced they had also factored RSA-
130-in a fraction of the time. Java would make cooperative efforts much easier through platform
independence.

So what does this have to do with malicious applets? One critical feature of the RSA efforts was the
voluntary participation. That is what made them cooperative efforts. The same sort of factoring
could be accomplished using a malicious applet. Such an applet would surreptitiously steal CPU
cycles from the machine of any Web user who hit its Web page. The applet would spin a thread on
the remote machine to run part of a factoring solution on that machine's CPU. After a sufficient
amount of work, a partial solution could be mailed back to a collection site for collation with
similar results from elsewhere.

There is no reason a CPU-cycle-stealing applet needs to work on factoring. It can perform any
work. Using such an applet, a Web miscreant could instantly upgrade his or her 486-DX2/66 into a
huge collective machine with the combined power of hundreds of workstations. Workstations
around the world could be automatically pressed into service. Imagine the dismay of a CEO who
discovers that her new Whiz-bang 4200+ has been helping compute results for a competitor. Or
imagine the legal ramifications in store for the owner of a government machine that inadvertently
helps a foreign national break an encryption algorithm. Or imagine a computer hardware
manufacturer who specs out a competitor's machine using a stealthy benchmark applet. The
possibilities are many.

CHAPTER SECTION:6

Section 6 -- Forging Mail


Many Net-savvy people are aware of a simple trick that allows users to forge electronic mail. This
is accomplished by talking directly to the SMTP daemon on port 25 of a server machine. The mail-
forging attack takes advantage of the fact that mail-serving hosts monitor port 25 for incoming
Simple Mail Transfer Protocol (SMTP) messages.

One of the Internet rites of passage is to telnet to port 25 and send fake mail to a friend. This game
is very well known; however, the scheme is easily debunked. The SMTP daemon actually marks
the forged mail with the IP number of the machine that connected to port 25, which makes it very
easy to discern which machine sent the mail.3 Spammers often forge mail headers so that the true
source of a spam message is hard to discern.
It is usually easy to detect forged mail by looking carefully at the header. The machine listed in the
From line should be the same as the Received: line. Most users and mail readers look only at the
From line; systems people know to look at both. Note that some legitimate mail may have differing
From and Received: lines, depending on how people send their mail. Here is an example of mail
forged by an author on his home machine (tigger.mediasoft.net) to his own work account. Note
how the From and Received: lines differ.

From god@heaven.com Wed Jul 24 19:33:56 1996


Return-Path: <god@heaven.com>
Received: from tigger.mediasoft.net by rstcorp.com (4.1/SMI-4.1)
id AA21199; Wed, 24 Jul 96 19:33:54 EDT
Received: from rstcorp.com (gem@tigger.mediasoft.net[205.139.200.246])
by tigger.mediasoft.net (8.6.12/8.6.9) with SMTP id SAA00966
for gem@rstcorp.com; Wed, 24 Jul 1996 18:30:31 -0400
Date: Wed, 24 Jul 1996 18:30:31 -0400
From: god@heaven.com
Message-Id: <199607242230.SAA00966@tigger.mediasoft.net>
Apparently-To: gem@rstcorp.com
Status: RO

This is forged mail.

Applets provide an interesting new twist on the standard approach to mail forging. Because applets
load across the network and run on a Web surfer's machine, a mail forging applet can cause the
standard SMTP daemon monitoring port 25 to report mail coming from the Web surfer's machine-
not the machine serving the applet. This can be leveraged to doubly forge mail. Imagine that Alice
hits a Web page and an applet is consequently run on her machine. By using Alice's machine to
forge mail from Alice-that is, to forge mail apparently both from her machine and from her account
on that machine-the doubly forged mail appears not to have been forged at all! With many standard
server configurations, this forging attack is possible. Figure 4.4 illustrates the difference between
the standard port 25 attack and the revised applet attack.

Figure 4.4 The usual mail-forging approach versus the applet-based


sendmail approach.

Because an applet runs on another host, it is possible to doubly forge mail so the
resulting message does not appear to be forged.

Forged mail of this sort can be sent to any email address that the applet's author chooses. The
message could, of course, be anything at all. To make this more concrete, imagine an applet that
sends an email death threat to the president of the United States (a felony) from the account and
machine of anyone naïve enough to browse a malicious Web page with Java enabled. Or, imagine
an applet that uses mail to spam hundreds of Usenet newsgroups with a fake legal advertisement
apparently posted from the victim's machine and account, immediately spawning a mail bomb
attack against the applet's victim from angry Usenet users bent on retaliation. There are many
variations on this theme.

Using threads, it is possible to forge mail in the background while doing something seemingly
innocuous in the foreground. This forging activity is currently possible; in fact, there are multiple
examples to be found on the Web. The possibly surprising fact is that the attack we have described
is completely within the security bounds of what SMTP and Java are allowed to do. Forging mail
like this is neither particularly sophisticated nor hard to implement. On the other hand, the havoc
that could be wreaked with a malicious applet of this sort is serious.

CHAPTER SECTION:7

Section 7 -- Killing Off the Competition


The Business Assassin applet discussed earlier combines two dirty tricks. The first trick is to spawn
a monitoring thread to watch for applets from another site. The second trick is to kill the threads of
any incoming applets. According to the rules, an applet should not be allowed to kill the threads of
other applets. Unfortunately, what should not be allowed to happen and what actually can happen
are not always the same. Implementation bugs in the security check for thread access for all JDKs
through 1.1.5 allow downloaded applets to access threads outside their own thread group. Killing a
thread is easy. Here is a code fragment that does it:

private static void ThreadMurder(Thread t){


t.stop();
}

You may wonder why the t.stop() method is inside the ThreadMurder() method. This code will kill
any thread t. It would be wise for the thread calling ThreadMurder() not to kill itself. A test inside
ThreadMurder() is an obvious way to protect the calling thread. All that is needed is a simple name
check. If the thread turns out to be checking itself, a decision is made to do nothing.
To make an applet that kills all threads not belonging to itself requires a bit more work. For clarity,
let's call this applet AssassinApplet. A recursive approach to AssassinApplet is probably best. The
basic outline is:

1. Starting with the current thread group, ascend to the root thread group.
2. From the root, recursively descend through all threads and thread groups below.
3. Kill each thread encountered (but not self).

This approach is both very nasty and very effective.

If coded as just shown, an AssassinApplet would be able to kill all other applets running when it
starts (a nice way to shut the NoisyApplet up!). It would also kill all applets that it comes across
after that. Since it is possible within our framework for the applet to name who should not be killed,
the AssassinApplet could run in tandem with other chosen applets. In fact, using the
AssassinApplet at all times is a half-baked alternative to turning Java off! Just run the
AssassinApplet once at the beginning of a session and after that, all applets encountered from then
on are guaranteed to be killed soon after arrival.

The good news is that we can defeat the ThreadMurder attack shown here using the try/finally
approach discussed earlier. The bad news is any hostile applet can, too.

CHAPTER SECTION:8

Section 8 -- Malicious Applets on the Web


The most extensive collection of malicious applets can be found on Mark LaDue's Hostile Applets
Home Page. LaDue does not follow our naming convention, which separates attack applets from
malicious applets. But in any case, all of the applets that LaDue has created are malicious applets.
In July 1998, a group of LaDue's newer malicious applets, those that allow creation of a
ClassLoader in Netscape 4.04 and 4.05, were leveraged to create an attack applet. LaDue's
ClassLoader subclassing when combined with the discovery of a ClassLoader bug by the Princeton
team (see Chapter 5) made possible a real attack.

LaDue's malicious applets perform the following hostile activities:

• Play a sound file forever (our NoisyApplet is adapted from this one).
• Kill a browser with a CPU-hogging attack.
• Consume all available memory on your machine.
• Spin endless threads to consume resources.
• Display many hundreds of large black windows.
• Combine many denial-of-service attacks (windows, threads, and sounds) into one payload.
• Pop a fake dialog box requesting sensitive information (username and password).
• Surreptitiously perform remote calculation and report results back to the server.
• Forge mail.
• Kill all applet threads (except for self).
• Send your browser to a URL over and over again.
• Obtain your username.
• Fill all disk space available to the browser.
• Create an AppletClassLoader (a good staging ground for more serious attacks).
• Exercise mystery methods (undocumented but available) that crash a browser.
• Misuse native methods through the Java API, resulting in a crash.
• Deny legitimate use of the audio system by retaining control over it.
• Steal information about the SystemPrincipal and create an impostor.
• Determine exactly which plugins a browser has with help from JavaScript.
• Steal information from a Java Wallet (including username and password).
• Carry out some social engineering in order to rewire the Help button of the Java Wallet.
• Cause a modem connected to an arbitrary serial port to dial.

The most interesting feature of LaDue's malicious applets is that source code is made available.
LaDue is clearly no proponent of keeping secrets!

No other author of malicious applets has been as prolific as LaDue, but notable among available
malicious applets are:

• An extremely simple recursive applet that pops the stack and crashes the VM (Naval
Postgraduate School)
• A mail forger and a file scanner written by Jim Buzbee
• An applet that abuses the redirect capability written by Ben Messander

Links to known malicious applets on the Web are maintained on the Java Security Hotlist.

CHAPTER SECTION:9

Section 9 -- The Implications


Unlike the technically adept attacks to be revealed in Chapter 5, these malicious applets are very
easy to write. There are malicious applets that play background sounds endlessly. There are
malicious applets that consume system resources, implementing denial-of-service attacks. There are
applets that forge electronic mail. There are even applets that kill other applets' threads.

Now that techniques are widely available on the Hostile Applets Home Page (among other places),
it is only a matter of time before malicious applets spread. Because malicious applet source code
has been put on the Web, hundreds of people can start to use and adapt the ideas. We have been
lucky that this has not happened yet. Perhaps we will continue to be lucky, or perhaps not.

As we have seen, an applet need not break into your machine in order to do malicious things.
Sometimes it is good enough to steal CPU cycles, or deny access to other sites. Malicious applets
come in all shapes and sizes. Defending against all of the possibilities is at best a daunting task.
Malicious applets may even play a role in undermining business on the Net. Recall the Business
Assassin applet that targets Gamelan. Other anti-business applets might send forged mail with
thousands of seemingly legitimate orders (resulting in thousands of expensive returns). Another
malicious applet could spam the Net with ads supposedly from you, should you be from the site of
a competitor. This could effectively cut your business off the Net when people respond with mail
bombs. It does not take too much foresight to fear the implications that these applets have for Net
commerce.

At least for the moment, malicious applets are not widespread; however, it is only a matter of time
before they are. Now is the time to look into ways to defend ourselves against them. Sun
Microsystems agrees: "We recognize the importance of providing people with some mechanism to
help them deal with hostile applets." Java 2 introduces mechanisms that can be used to help address
the problem.

Attack Applets:
Exploiting Holes in the Security
Model
There is a tightrope to walk in this chapter. You should understand the problems encountered with
Java, so you know how things can go wrong, especially if you are charged with designing security-
critical systems, administering a large number of Java users, or making business decisions that
depend on Java security issues. But it is not the intent of this book to give the bad guys a manual
for invading your computer. Although we discuss Java security problems, we hope you forgive the
omission of details necessary to exploit these problems.

Just for the record, we do not believe in security by obscurity. If we did, we would not have written
this book at all. However, we don't believe in publishing exploit scripts and aiding and abetting
attacks by inexperienced would-be crackers, either. Serious Java attacks have yet to escape the lab,
and we want to do our part to keep it that way.

In the early days of Java, Sun Microsystems and the rest of the Java industry hyped Java as
completely secure [Sun Microsystems, 1995]. This was really no surprise. They still have a lot to
gain if you believe them and jump aboard the Java bandwagon without even considering the risks
of doing so. It's true that Sun Microsystems, Netscape, Microsoft, and others have gone to great
lengths to make their Java implementations as secure as possible. That's all well and good, but you
don't want effort-you want results. To this day, the question remains: Is Java safe enough to use?
This chapter examines all of the serious security flaws that have been found in Java so far. By
serious, we mean attacks based on these flaws could go beyond the annoyance or denial-of-service
attacks of Chapter 4, "Malicious Applets: Avoiding a Common Nuisance." These attacks could
corrupt data on your hard disk, reveal your private data to third parties, turn your machine into a
hostile listening post, or infect your machine with a virus. By exploiting some of the vulnerabilities
discussed here, a cracker could attain the ultimate cracker goal-complete control of your machine.

Java vendors are very quick to fix any problems that are discovered in Java. In fact, vendor
response to the discovery of a new Java security hole far surpasses the usual response to non-Java-
related security problems posted to Bugtraq and other mailing lists run by security professionals
(Bugtraq archives are available at www.geek-girl.com/bugtraq/). In terms of Java, the penetrate-
and-patch machine is smoothly oiled (not that it represents the best approach to security, but that's
another issue). Rest assured that the problems we discuss in this chapter have been fixed in the
latest JVMs, including those packaged in Java-enabled browsers. That means if you're using an up-
to-date version of your favorite browser, these specific problems won't affect you. On the flip side,
if you're using an older browser like Netscape 2.x/3.x or Internet Explorer 3.x, this chapter provides
enough information about serious attacks that you really should upgrade immediately. Browsers are
not patched; they are made obsolete through accelerated release of new versions. Using an old
browser to surf the Web is like wearing a "kick me" sign to a fraternity party.

Though these specific attacks are not likely to be your problem since they have been fixed, they
indicate what sorts of things can go wrong and what the consequences are when things do go
wrong. If more Java security problems are found in the future, they're likely to be similar to the
ones presented here. Hopefully the industry can learn from its old mistakes and avoid reintroducing
old holes (as often happens in computer security).

Most of these problems were trivial to fix once they were discovered. Removing security bugs is
like removing needles from a haystack: It's hard to find the needles, but they're easy to remove once
you know where they are. To push the analogy a bit: It's obviously much better to find the needles
before they stick you. This principle motivates our Java security research.

Chapter Five Sections


1. Implementation Errors or Specification Errors?
2. Attack Applets
3. What Applets Aren't Supposed to Do
4. A Chronology of Problems
5. Jumping the Firewall
6. Slash and Burn
7. You're Not My Type
8. Applets Running Wild
9. Casting Caution to the Wind
10. Tag-Team Applets
11. Big Attacks Come in Small Packages
12. Steal This IP Number
13. Cache Cramming
14. Virtual Voodoo
15. The Magic Coat
16. Verifying the Verifier
17. The Vacuum Bug
18. Look Over There
19. Beat the System
20. What These Problems Teach Us

Attack Applets: Exploiting Holes in the Security Model


CHAPTER SECTION:1

Section 1 -- Implementation Errors or Specification


Errors?

One question we are commonly asked is whether Java's security woes are due to simple bugs or
reflect deeper design problems. The answer is a bit complicated, as we shall see.

Software Engineering Meets Internet Time

Software that is properly engineered goes through a standard process from requirements design,
through detailed specification, to actual implementation. In the world of consumerware (software
created for the mass consumer market, like browsers and JDKs), pressure to be first to market and
retain what is known as "mind share" compresses the development process so much that software
engineering methods are often thrown out the window. This is especially true of testing, which
regularly ends up with no scheduled time and few resources. An all too common approach is to
leave rigorous testing to users in the field (sometimes even paying users when they find bugs!). We
think this is just awful.
The Internet time phenomenon has exacerbated the software engineering problem. These days,
Internet years rival dog years in shortness of duration (the standard ratio is seven dog years to one
regular year). So three months of regular time are currently equivalent to a complete Internet
"year." Given the compressed development schedules that go along with this accelerated kind of
calendar, the fact that specifications are often very poorly written (if they exist at all) is not
surprising. The authors commonly encounter popular consumer-oriented systems that have no
specifications. Java suffered from this problem in its early years as well. Fortunately, Java does
have an informal specification today. That's always a good start.

One of the most common misconceptions about Java security holes is that they are all simple
implementation errors and that the specification has been sound and complete since day one.
Threads in the newsgroup comp.lang.java.security and other newsgroups often repeat this fallacy as
people attempt to trivialize Java's security holes. The truth is that many of the holes described in
this chapter are simple implementation bugs (the code-signing hole from April 1997 comes to
mind-see The Magic Coat later in the chapter), but others, like problems discovered in Java class
loaders, are not. Sometimes the specification is just plain wrong and must be changed. As an
example, consider how the Java specification for class loading has evolved.

Often it is hard to determine whether a security hole is an implementation problem or a


specification problem. Specifications are notoriously vague. Given a vague specification, who is to
blame when a poor implementation decision is made? Specifications are also very often silent; that
is, when a hole is discovered and the specification is consulted, there is nothing said about the
specific problem area. These sorts of omissions certainly lead to security problems, but are the
resulting problems specification problems or implementation problems?

In the end, the holes are fixed, regardless of whether they are implementation bugs or design-level
problems. 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 flexible access control in Java 2
is a case in point. Implementing a code-signing and access-control system is nontrivial, and the
code is certainly security-critical. Other examples are serialization and remote method invocation
(RMI). Subtle security problems are likely to be discovered in these and other new Java
subsystems.

Discovering Holes

Why is it that all the known attack applets covered in this chapter were discovered by good guys
and not bad guys? The quick but unsettling answer is: pure luck. The Princeton team and other Java
security researchers are not the smartest people in the world (sorry guys), and the holes uncovered
in Java so far do not require years of advanced training to find. There is no reason that malicious
crackers could not discover such holes for themselves. The Java industry has been fortunate that the
people who usually discover Java security problems are honest and want to see Java improved so
that it is safer to use. Also fortunate is the punctuality and accuracy of typical vendor response.
So how are holes usually discovered? Most often, the scenario goes something like this.
Researchers discuss where potential flaws may lie by thinking about what is difficult to implement
properly. Occasionally, researchers notice peculiar or surprising behavior in their work with Java
and get an idea about what to investigate. The next step is to take a close look at the Java source
code (for the VM and API classes) or the binary code if no source code is available. Sometimes,
errors are obvious and exploits are easy. Other times, experimentation is required to turn a potential
flaw into a real exploit. All of the holes described in this chapter can be exploited using attack
applets. That means the holes covered here are not esoteric flaws that are impossible to exploit.
They are sometimes-subtle flaws that have been turned into full-fledged attacks.

Holes and Exploits

Every Java hole described in this chapter has an accompanying exploit. Another way of putting this
is that there is an attack applet (the Java form of an exploit script) for each hole discussed here.
However, the one-to-one correlation found in this chapter does not imply that it is necessary for
every security hole to have an exploit. Holes are just vulnerabilities. Sometimes a hole will be
recognized as a hole but cannot be exploited by itself. In these cases, multiple holes together create
an exploit.

Think of attacking a system as climbing up a cliff. When you reach the top, you have successfully
completed an attack. A security hole can be likened to a piton in the cliff with a piece of rope
attached. Sometimes one piton is enough to help a climber make it to the top (especially if the
climber is an experienced veteran). Other times, more than one piton may be needed. The holes
discussed in this chapter have exploits of both categories. A majority of the attack applets require
only one hole, but sometimes an attacker must leverage other weaknesses to exploit a hole. (A
perfect example of the latter category is the Beat the System hole of July 1998.)

CHAPTER SECTION:2

Section 2 -- Attack Applets


It is important to reemphasize that the attacks described in this chapter are not hypothetical; each
has been implemented by either the Secure Internet Programming team (SIP) at Princeton
University or other researchers. Each was successfully used to break into a machine in the
laboratory. The Princeton team, who have discovered a majority of known holes, choose not to
release the resulting attack applets onto the Net. Other researchers, especially consultants, tend to
release their attacks.

Attack applets are the most dangerous kind of hostile applets. They do more than simply annoy or
deny service. The end result of an attack applet is the same as being hacked by a cracker: Your
system is wide open for unauthorized access.
According to both our research and that of the CERT Coordination Center (an organization that
keeps track of computer security violations on the Internet), there have been no confirmed reports
of loss due to the attacks described in this chapter. There are, however, a few cases of attacks
possibly carried out with applets. It is, of course, impossible to rule out the possibility of attacks
that haven't been discovered or that haven't been reported. The lack of reports indicates that the
number of attacks, if any, has been small. Successfully implemented attack applets probably
haven't occurred in the wild, but there can be no guarantee that one won't show up tomorrow. The
danger is real enough that CERT recommends people disable Java when using particular versions
of popular browsers [CERT, 1996a; CERT, 1996b].

CHAPTER SECTION:3

Section 3 -- What Applets Aren't Supposed to Do


Chapter 2, "The Base Java Security Model: The Original Applet Sandbox," and Chapter 3, "Beyond
the Sandbox: Signed Code and Java 2," discussed the Java security model at length. Java's
designers tried to ensure that untrusted or partially trusted applets could not misbehave by placing
them in an adjustable sandbox. For a concise listing of things that untrusted Java applets should not
be allowed to do, see Chapter 2. It is also worth reading the "Frequently Asked Questions-Java
Security'' Web page served by Sun Microsystems at java.javasoft.com/sfaq. In order to provide
concrete examples of Java security policies that work, Sun's Security FAQ page includes pointers
to a number of applets that cannot get around Java security. The good news is that some
straightforward approaches to breaching security will fail. The bad news is that crackers usually
don't give up after the straightforward approach fails. Fortunately, neither do security researchers.

It is always interesting to get an objective outsider's opinion about Java security. That is probably
one of the reasons you are reading this book.Appendix A, "Frequently Asked Questions: Java
Security, Java versus ActiveX," includes a hard copy of two of the Princeton Secure Internet
Programming team's Java Security FAQs. An up-to-the-minute version of the FAQs can be found
at www.cs.princeton.edu/sip/java-faq.html.

CHAPTER SECTION:4

Section 4 -- A Chronology of Problems


To date, 16 serious security problems have been discovered in implementations of Java. Table 5.1
lists the holes and their accompanying exploits by name. A brief chronology follows that describes
each flaw. These flaws will be discussed in more detail later in this chapter. Some of these flaws
allow full system penetration. This means that an attacker could exploit them to do literally
anything to your machine, including corrupting your data, reading your private data, injecting a
virus, or leaving a trapdoor to reenter your machine at will.
Table 5.1 Attack Applets that Exploit Holes in the System

DATE ATTACK APPLET

February 1996 Jumping the Firewall

March 1996 Slash and Burn

March 1996 Applets Running Wild

May 1996 Casting Caution to the Wind

June 1996 Tag-Team Applets

June 1996 You're Not My Type

July 1996 Casting Caution to the Wind (reprise)

August 1996 Big Attacks Come in Small Packages

February 1997 Steal This IP Number

February 1997 Cache Cramming

March 1997 Virtual Voodoo

April 1997 The Magic Coat

May 1997 Verifying the Verifier

July 1997 The Vacuum Bug

August 1997 Look Over There

July 1998 Beat the System

February 1996: Drew Dean, Edward Felten, and Dan Wallach at Princeton discovered a flaw in
Java's networking software, affecting Netscape Navigator 2.0. This flaw was postulated
independently by Steve Gibbons. It could be exploited to launch security attacks on other machines
on a private network. This flaw was fixed in Netscape Navigator 2.01. The resulting attack is called
Jumping the Firewall. See page 147. This attack resulted in one of the two Java-related CERT alerts
[CERT, 1996a].

March 1996: David Hopwood at Oxford University found a flaw that allows an attack that tricks
Java into treating the attacker's applet as trusted code. This flaw allowed full system penetration. It
affected Netscape Navigator 2.01 and was fixed in Netscape Navigator 2.02. The resulting attack is
called Slash and Burn. See page 153.

March 1996: The Princeton team (Dean, Felten, and Wallach) found a bug in the Java byte code
Verifier and a flaw in the class-loading mechanism. Together, these allowed full system
penetration. This problem affected Netscape Navigator 2.01 and was fixed in Netscape Navigator
2.02. The resulting attack is called Applets Running Wild. See page 158. This attack resulted in the
second of the two Java-related CERT alerts [CERT, 1996b].
May 1996: Independent consultant Tom Cargill, working with the Princeton team (Dirk Balfanz,
Dean, Felten, and Wallach) found a flaw in the implementation of the Java interpreter. This flaw
allowed full system penetration. It affected Netscape Navigator 2.02 and Microsoft Internet
Explorer 3.0beta1, and was fixed in Navigator 3.0beta3 and Explorer 3.0beta2. The resulting attack
is called Casting Caution to the Wind. Seepage 163.

June 1996: Hopwood found another flaw in the interpreter that again allowed full system
penetration. This flaw affected Netscape Navigator 3.0beta3 and was fixed in Navigator 3.0beta4.
The resulting attack is called Tag-Team Applets. See page 165.

June 1996: Balfanz, Dean, and Felten found a flaw in Java's implementation of array types that
allowed full system penetration. This flaw affected Netscape Navigator 3.0beta5 and was fixed in
Navigator 3.0beta6. The resulting attack is called You're Not my Type. See page 156.

July 1996: Cargill, Balfanz, Dean, and Felten found another implementation flaw in the Java
interpreter. This flaw allowed an attacker to mount some attacks on network services on other
private-network machines. This flaw affected Netscape Navigator 3.0beta5 and was fixed in
Navigator 3.0beta6. This attack is also called Casting Caution to the Wind. See page 163.

August 1996: Balfanz and Felten found a flaw in Microsoft's Java implementation. The flaw
allowed code in an attack applet to become a member of a security-critical Java package, thus
gaining the ability to change various security parameters. This in turn gives the applet full access to
the target machine's files and the network. This flaw affected Microsoft Internet Explorer 3.0beta3
and was fixed in Explorer 3.0beta4. The resulting attack is called Big Attacks Come in Small
Packages. See page 167.

February 1997: A pair of British hacker/consultants, Ben Laurie and Major Malfunction,
discovered two separate flaws, one that worked only against Netscape Navigator 3.x and the other
that worked only against Microsoft Internet Explorer 3.x. The Microsoft flaw was much more
serious than the Netscape flaw. The fact that the attacks were browser specific serves to emphasize
that different vendors have different approaches to implementing Java security. To be sure, both
vendors are coding to the same specification, but there are many ways to skin a cat. The two attacks
are called, Steal This IP Number and Cache Cramming, respectively. See pages 169 and 171.

March 1997: Security researchers at JavaSoft discovered a subtle error in the JVM and
immediately patched it. This illustrates how serious JavaSoft is about security. The error was deep
inside the Virtual Machine code and affected all browsers. The attack is called Virtual Voodoo.
Seepage 172.

April 1997: Soon after the release of JDK 1.1, the Princeton team discovered a serious flaw in the
newly implemented code-signing system. The flaw allowed an attack applet to take on maximum
privilege allocated inside a system. Because only the HotJava browser supported JDK 1.1, it was
the only browser affected by the flaw. This attack is called The Magic Coat. See page 172.

May 1997: The Kimera group at the University of Washington, led by Professor Brian Bershad,
created a byte code Verifier of their own using formal methods and strict software engineering
processes. In order to test their implementation, the Kimera group created an automatic test harness
that generated millions of tests by mutation. They then tested their Verifier against commercially
available Verifiers. In the course of testing, they identified 27 errors in various commercial
Verifiers. One error was severe enough to warrant a patch. The others were patched in later releases
of the JDK. This set of attacks is called Verifying the Verifier. See page 174.

July 1997: Sun's response to the Verifier errors in May (which downplayed their severity)
prompted the Kimera group to create an exploit that attacked Netscape Navigator 3.x. The attack
was able to gain access to protected information in the browser's memory space, including private
keys used in setting up secure socket layer (SSL) sessions. This attack is called the Vacuum Bug.
See page 177.

August 1997: Creative Concepts consultant Ben Mesander discovered a weakness in the way
HTTP redirect operations were handled by Java. The weakness was particularly obvious in
Microsoft Internet Explorer 3.x but was also present (in a subtler fashion) in Netscape Navigator
3.x. This attack is called Look Over There. See page 178.

July 1998: Balfanz, Dean, Felten, and Wallach found a security flaw in the ClassLoader
implementation of JDK 1.1 and JDK 1.2beta3 that allows a malicious ClassLoader to override the
definition of built-in "system" types like java.lang.Class. When properly abused, this can lead to a
type confusion attack that completely compromises security. The flaw requires use of a secondary
flaw to gain a foothold. Mark LaDue had earlier discovered such a flaw in Netscape 4.0x's Security
Manager. Thus, Netscape 4.0x is exploitable, while other Java VMs (Sun's and Microsoft's) do not
appear to be exploitable even though they contain the same flaw. This attack is called Beat the
System. See page 182.

The rest of this chapter describes these flaws in more detail.

When Holes Happen

The chronology presented here has an interesting meta-level property. Both in August 1996 and in
August 1997 several months followed in which no major security holes were discovered.
Interestingly, the period of six months in late 1996 was of long enough duration that some Java
evangelists began to claim that all Java holes had been discovered and that Java was completely
secure. They were wrong.

First of all, anyone who claims any program or programming language is completely secure should
be viewed with suspicion. There is no such thing as 100-percent security; the best we can do is
minimize risk. This goes for Java as well as any other computer system. Fortunately, Java mitigates
many risks that competing systems of mobile code like ActiveX and JavaScript do not (see Chapter
1, "Mobile Code and Security: Why Java Security Is Important," for details). This makes Java more
secure than those systems, but it does not make Java perfectly secure. Short of proving Java correct
(which is unlikely ever to happen given the state of formal methods), we must live with potential
security holes.
Second, the pattern in the chronology corresponds with major releases of the JDK. The gap
between August 1996 and February 1997 included the time period when JDK 1.02 was in common
use and JDK 1.1 had not yet been released. As soon as JDK 1.1 was released, some new holes were
discovered. The same sort of gap appeared as Java 2 slowly trickled out. This reflects two facts: 1)
each major release includes much new functionality, and 2) Java is complicated, and complicated
systems always include room for error. From these facts, it is natural to infer that the discovery of
holes will coincide with major releases.

CHAPTER SECTION:5

Section 5 -- Jumping the Firewall


In the first problem, an attack applet launches network security attacks on other machines. This is
something that an attacker could already do before Java came along. The twist is that by embedding
the attack into an applet, the bad guy makes the attack come from the machine of an innocent
bystander. Example: You're sitting at your desk, happily browsing the Web, and without realizing
it, your machine is trying to penetrate the security of a machine down the hall.

This kind of confusion is reason enough to use Java as the penetration vehicle, but the culprit has
an even better reason for using Java. Many corporate networks protect themselves from Internet
intrusion through the use of a firewall. (See Figure 5.1.) If your firewall is well configured, it
prevents the mischievous cracker from making direct use of the network to probe the defenses of
your machines. The firewall does this by blocking certain types of network traffic from entering the
corporate network.

Figure 5.1 A firewall stands between your internal network and the rest of
the Internet.

It screens every packet of data coming across the network, allowing only
certain kinds of packets through. A firewall is only as effective as the policy it
implements.

A Java applet, though, doesn't look suspicious to most firewalls. (This was more true in 1997 than
it is today. We discuss the state of the art in applet blocking in Chapter 6, "Securing Java:
Improvements, Solutions, and Snake Oil.") Many firewalls examine the type of packet, not the
contents of the packet. For some firewalls, to block Java applet traffic, a system manager would
have to block all Web traffic.1 The fact that your browser requested the applet makes the firewall's
job that much trickier. If the applet can open network connections to other machines, it can connect
from your machine to another machine behind the firewall. In the current case, the attack originates
from the inside rather than the outside. Since the firewall only filters traffic coming from the
outside, it is helpless to prevent this sort of attack. This is especially dangerous since many sites
have strong firewall protection, but almost no protection against attacks from the inside. In security
circles, these sites are jokingly referred to as "crunchy on the outside and chewy in the middle."

The people who designed Java-enabled browsers thought of the possibility of inside-the-network
attacks, so they made a security rule to prevent it. The rule states:

An applet may not open a network connection, except back to the server from which it came.
If enforced properly, this rule stops any network probing by applets. Netscape Navigator 2.0 did not
enforce this rule properly. In order to understand what went wrong, you need to understand how
machines are named on the Internet.

Internet Naming

Like people, machines on the Internet need to have names to identify them. Specific names help
machines send messages across a network. These names are also numeric addresses. Because these
numbers are often difficult to remember, there are two layers of network addressing in the Internet.
The Internet Protocol (IP) uses only numeric addresses to communicate between machines. The
Domain Name System (DNS) keeps track of how the user-friendly names correspond to the IP
numbers used to establish a machine's low-level connections.

An IP address is just a number. For example, the Web server at JavaSoft has this numeric address:
11001110000110100011000001100100 in binary notation. IP addresses are often written in
decimal form, which looks like 206.26.48.100. When the computers that make up the Internet talk
to each other, they identify themselves with the numeric IP addresses. Computers deal naturally
with numbers like this, but they are, to say the least, not very user-friendly.

The other sort of Internet names, DNS names, are made for people. They look like java.sun.com, or
sandbox.rstcorp.com. These names are made up of often intelligible words strung together with
dots to separate them. DNS divides the world up into domains like sun.com (Sun Microsystems)
and cs.princeton.edu (the Princeton University Computer Science department). Each domain
corresponds to a single administrative entity. It is up to that entity to define names that end in its
domain name. For example, the cs.princeton.edu domain is free to define names like
elvis.cs.princeton.edu. Anyone can create his or her own domain by registering with an
organization called InterNIC and paying a modest fee.

The owner of each domain is responsible for providing two DNS server machines that respond to
queries about DNS names inside that domain. For example, if someone wants to know the IP
address of elvis.cs.princeton.edu, he or she can ask one of the DNS servers for cs.princeton.edu.

A single DNS name might refer to several IP addresses. There are two reasons for this. First, a
machine might be connected to more than one network, with a separate IP address for each of its
connections. Second, there might be several machines providing the same service. For example,
espn.sportszone.com might actually correspond to several machines, all providing identical
services.

Sometimes several DNS addresses refer to the same IP address. For example, a company's Web
server www.rstcorp.com and its FTP server ftp.rstcorp.com might actually be the same machine.
This makes sense because management might later want to move the two functions onto separate
machines. Using two separate names allows them to keep this flexibility.

What Went Wrong: The Java DNS Security Bug

To enforce the rule that an applet can connect only to the server from which it originated, the
implementers of Java needed a way to check whether the machine an applet wanted to reach was
the same as the machine that the applet came from. They did this as follows:

• Use DNS to translate the name of the Web server into a list of IP addresses.
• Use DNS to translate the name of the machine the applet wants to connect to into a list of IP
addresses.
• Compare the two lists. If any address appears in both lists, declare the two machines are the
same and allow the connection. If not, declare they are different and refuse the connection.

This way of using DNS to authenticate a host is illustrated in Figure 5.2. Though this approach
sounds good at first, it turns out to be too permissive.

Figure 5.2 How Java originally used DNS to make sure that an
applet attached only to the machine that served it.

The problem is that the check is too lenient.

The following scenario describes what can go wrong. Figure 5.3 shows the scenario visually.
Figure 5.3 How the DNS security bug allows an applet to jump a site's
firewall.

The figure shows several different snapshots arranged in order of occurrence.

Imagine that a bad guy wants to attack a machine called target.victim.org, with the IP address
10.10.10.2. The bad guy sets up a Web server calledwww.attacker.org, with IP address
172.16.16.16; then he waits. An unsuspecting person, surfing the Web on stooge.victim.org (IP
address 10.10.10.1), happens to visit the attacker's Web site. The site contains a Java applet written
by the attacker. The applet is downloaded to stooge.victim.org and run.

The applet asks to create a network connection to bogus.attacker.org. Because that name is in the
attacker.org domain, the attacker's DNS server is asked to provide an IP address for that machine
and is free to provide any IP addresses it likes. The attacker's DNS server slyly returns the pair of
addresses (10.10.10.2, 172.16.16.16). Because that list contains the address of the attacker's Web
server (172.16.16.16), Java erroneously concludes that www.attacker.org and bogus.attacker.org
are really the same machine, so it allows the connection to go ahead.

Unfortunately, after verifying the connection is allowed, Java connects to the first address on the
list, 10.10.10.2, or target.victim.org. The attacker has achieved his goal: to connect to the target
machine.
What does the attacker do next? The attacker can systematically probe the defenses of the target
machine, looking for weaknesses. Sophisticated tools such as SATAN, Ballista, and ISS even exist
to automate this part. If the attacker finds a weakness, the victim could be in big trouble.

The Fix

This problem was fixed by simply changing the criterion by which Java decides to allow a
connection. The new approach is to store the IP address of the Web server, and allow a connection
only to that IP address. This simple and complete solution is implemented in Netscape Navigator
versions beginning with 2.01 and all Java-enabled Microsoft Internet Explorer versions. It is no
longer possible to jump the firewall with Java using the DNS bug; however, it is important to make
sure that you are not using the unpatched Netscape 2.0.

The Reaction

The announcement of this flaw triggered a flurry of press reports, beginning with a story in USA
Today. Reporters learned of the flaw from a brief message in the comp.risks forum. The
discoverers of the attack were surprised to learn that many reporters monitor comp.risks.

It turned out that the existence of this flaw had been postulated independently by Steve Gibbons
about four weeks before the announcement. Steve Gibbons had reported the bug to Sun
Microsystems, but it was not fixed. After the USA Today article, Sun Microsystems and Netscape
said they would fix the bug within days. It was fixed quite quickly.

The security researchers who uncovered the DNS attack were surprised to see that the press treated
the news as a business story rather than as a technical story. This was probably a naïve point of
view. These days, technology reporting, even when discussing noncommercial technology, seems
to be considered a branch of business reporting.

It was also surprising to see that many news organizations repeated a story that they had read
elsewhere without contacting the parties involved and apparently without reconfirming any of the
facts! As usual, when information is heard and then repeated, small inaccuracies creep in at each
stage. It was sometimes possible to figure out who had copied the story from whom, by tracking
small inconsistencies.

The USA Today story also triggered a blip in the stock market. Netscape's stock price dropped
significantly on the day the story appeared. CNN and the Nightly Business Report attributed the
drop to the announcement of this flaw, although there were other factors (for example, the
expiration of the post-IPO embargo on insider sales) also driving down Netscape's stock that week.
In any case, the stock bounced back when it became clear that the product was not irretrievably
broken.
CHAPTER SECTION:6

Section 6 -- Slash and Burn


The second set of attacks involves Java code that passes itself off as belonging to the browser. In
early versions of the JDK (before Java 2), code that came with the browser was assumed to be safe
(see Chapter 2 and [McGraw and Felten, 1996]). The original built-in code distinction was
scrapped with the introduction of code signing, and these days very little code is trusted (see
Chapter 3). In any case, the Slash and Burn fraud allows the malicious code access it would not
ordinarily have. It could, for example, access files on the local disk.

In order to properly understand this attack, you need to understand how Java works. In particular,
examine how Java accesses its own code on the browser's local disk.

Where Java Code Comes From

When a Java applet runs, many Java classes (pieces of Java code) are loaded and run. Some applet-
related classes are loaded by the applet, using the Web server. Other classes are part of the browser
itself. Browser-related code is stored with the browser on the local disk. Netscape, for example,
keeps its Java class files zipped up in an archive called classes.zip. When Netscape is installed, the
class archive needs to be put somewhere special, like /usr/local/lib/netscape on Unix machines.
Because the browser classes were considered part of the trusted browser program, they were given
more privileges. (This is no longer the case.)

In general, before JDK 1.1, Java treated code loaded from the local disk as trusted, and code loaded
over the Net as untrusted. That meant if an attacker could somehow get some malicious code
loaded from the local disk, the attacker was home free. The original system was changed
significantly with the introduction of JDK 1.0.2, which stopped treating code loaded off the disk as
trusted. This change was made behind the scenes with little fanfare. As the Cache Cramming attack
(explained later) shows, many people were confused by the change.

From our discussion of the Java class loader in Chapter 2, we know that when Java needs to find a
piece of code, say, for a class MyClass, it first looks on the local disk for a file called
MyClass.class. If Java fails to find an appropriate file on the local disk, then it tries to fetch the file
from the Web server that originally provided the applet.

We've glossed over one key issue at this point: How does Java know what class to look for? The
answer is that a class is only loaded when it is mentioned by another class already resident. This is
called dynamic loading. The name of the mentioned class is stored in the Java code for the
mentioning class.

Java classes have names like security.book.chapter5. When the Java system wants to look up a
class on the disk, it translates the dots in the class name into backslashes. The name
security.book.chapter5 becomes security\book\chapter5.2 This transformed name is the filename
used to search for the file on the local disk.

What Went Wrong: Dots and Slashes

If a bad guy wants to pass off a piece of code as trusted (in systems before JDK 1.0.2), two steps
must be carried out: 1) Get the malicious code onto the victim's disk, and 2) Trick the victim's
browser into loading it.

The first part, getting code onto the victim's disk, isn't as difficult as it sounds. For example, some
machines have public FTP directories, where anyone can put a file. Alternatively, if the victim is
using a shared, public machine, the attacker could get an account on that machine and put the file in
that account's home directory.

Perhaps the most effective way to inject code is to take advantage of the browser's cache. Most
Web browsers keep on-disk copies of recently accessed files. This allows repeated accesses to the
same Web documents without continually downloading the documents. Unfortunately, it also gives
a malicious applet a way to get a file onto the victim's machine. The applet could load the file
across the Net, pretending that it was an image or a sound file. Once this was done, the file would
be on the victim's disk in the cache. If the applet knew how the browser organized its cache, it
would know where on the victim's disk the file resided. This sneaky trick makes a second
appearance in the Cache Cramming attack as well (seepage 171).

Once the file is on the victim's disk, the attacker tricks the victim's browser into loading the file.
Since the browser only looks up classnames in relation to the current directory, the attacker would
have to place a file into the victim's working directory. Filename lookup is relative because Java
classnames cannot start with a dot; therefore, the translated name cannot start with a backslash.

David Hopwood discovered that Java 1.0.1 and Netscape Navigator 2.01 erroneously allowed a
classname to start with a backslash. Such a classname could reference any file on the system, not
just those files associated with the browser. For example, a class named
\programs.browser.cache.file407 would be looked up on the local disk as
\programs\browser\cache\file407.

This trick could be used to cause any file on the local disk to be loaded as Java code. Because code
loaded from the local disk was trusted (pre-JDK 1.0.2), it could proceed to illegally access the local
system. This attack allows full system penetration-the bad guy can do anything at all on the victim's
machine.

The Fix

This problem was fixed in Netscape Navigator 2.02 and in all Java-enabled versions of Microsoft
Internet Explorer. The fix was simple: Prohibit classnames from starting with backslashes (or
slashes, as the case may be). It is no longer possible to execute impostor code using the Slash and
Burn attack.

CHAPTER SECTION:7

Section 7 -- You're Not My Type


As discussed in Chapter 2, the most common kind of serious security problem in Java involves type
confusion. A type-confusion attack confuses the Java system about the types of data objects it is
manipulating.

The Java system treats objects as blocks of memory. Allocated memory contains the data fields of
all objects, lined up one after the other. When a Java program has a reference to an object, what it
really has internally is a pointer to the memory address storing the object. You can think of the
pointer as tagged with a type that says what kind of object the pointer is pointing to.

As mentioned in Chapter 2, every aspect of Java security depends critically on the type-safety of
the language. This means that if Java is going to be secure, it has to make sure that all pointers are
properly tagged; that is, the tag must match the actual type of object that is being pointed to.

In a type-confusion attack, a malicious applet creates two pointers to the same object-with
incompatible type tags. When this happens, the Java system is in trouble. The applet can write into
that memory address through one pointer, and read it through another pointer. The result is that the
applet can bypass the typing rules of Java, completely undermining its security.

Figure 5.4 shows a type-confusion attack at work. The applet has two pointers to the same memory:
one pointer tagged with type T and one tagged with type U. Suppose that T and U are defined like
this:

class T {
SecurityManager x;
}

class U {
MyObject x;
}

Now the applet can run code like this:

T t = the pointer tagged T;


U u = the pointer tagged U;
t.x = System.getSecurity(); // the Security Manager
MyObject m = u.x;
The result is that the object ends up with a pointer, tagged as having type MyObject, to the memory
representing Java's Security Manager object. By changing the fields of m, the applet can then
change the Security Manager, even though the Security Manager's fields have been declared
private.

While this example showed how type confusion can be used to corrupt the Security Manager, the
tactic may be exploited to corrupt virtually any part of the running Java system.

Figure 5.4 Type-confusion attack.

Two of the objects in the reference table, t and u,


are supposed to be of different types, but actually
reference the same object in memory.

An Example of Type Confusion

Drew Dean discovered a typical type-confusion attack, based on Java's handling of array types.
Java allows a program that uses a type T to use the type array of T. These array types are not
explicitly declared by the programmer, but exist automatically. The Java Virtual Machine defines
them automatically when they are needed.

These array types are defined by the VM for internal use. Java gives them a name beginning with
an open square bracket ([). As this character is not allowed to be the first character of a
programmer-defined classname, there is no danger of conflict.

Dean discovered, however, that in Netscape Navigator 3.0beta5, a Java byte code file could declare
its own type name to be one of the special array type names. Attempting to load such a class would
generate an error, but the Java VM would install the name in its internal table anyway.

This redefined one of Java's array types and created a classic type-confusion scenario: Java
considered the object an array, but it actually had some other type. The result was full system
penetration. This problem was fixed in Navigator 3.0beta6.

The Type-Confusion Toolkit

The Princeton team, as a feasibility demonstration, created a toolkit that allows any type-confusion
attack to be turned into a disarming of Java's security. In other words, the toolkit serves as a way of
turning a small security breach into a complete system penetration. The type-confusion toolkit has
not been released to the public and is considered too dangerous to describe in any detail here. The
toolkit was recently revised to work against Java 2 systems.

CHAPTER SECTION:8

Section 8 -- Applets Running Wild


The next security problem is the Princeton class-loader attack. This was the most widely publicized
of all Java security breaches. The problem was caused by mistakes in the way the Java system
integrated separate pieces of code. By corrupting this integration or linking process, an attacker
could break through Java's security and do anything at all. To help better understand this issue, the
following section looks more closely at how Java manages the dynamic-linking process.

Linking

A Java program is composed of several separate pieces called classes. Each class is stored in a
separate file, and the Java system uses a just-in-time strategy to load each class only when it is first
needed. Just-in-time loading allows Java applets to start running quickly, without waiting for the
entire applet to be pulled across the Net. It does have one drawback, however: A running applet is
usually incomplete. When an applet is built from several code pieces, the system has to be clever
enough to make sure that the right pieces are attached in the right places.

A Java class file contains a series of instructions telling the Java system how the class should
behave. The instructions sometimes reference other classes by name. Since classes are stored
separately, the Java system translates each name into the identity of another class. This may involve
loading the mentioned class across the Net.

The core Java system does not do this translation itself, but outsources it to Java objects called
Class Loaders. Outsourcing in this way allows programmers to create their own class loaders,
extending Java's linking mechanism.

The interaction between a Class Loader and the core elements of Java is simple. When Java needs
to determine which class corresponds to which name, the following steps are followed:

1. Java calls the Class Loader's loadClass method, passing it the name to look up.
2. The Class Loader consults its internal dictionary (which can include a list of built-in classes)
to see whether a class with that name already exists. If one exists, that class is returned.
3. If the Class Loader does not have a class with the requested name, it tries to find one.
Usually, it does this by fetching the byte code for the class across the Net.
4. After getting the byte code for the class, the Class Loader calls a special method called
defineClass to turn the byte code into a usable class.
5. When defineClass is finished, the Class Loader returns the resulting class to Java.
The Class Loader's loadClass method returns the class that corresponds to the name being looked
up.

There are usually several class loaders in operation. When Java needs to translate a name, it asks
the Class Loader that originally loaded the class referencing the name. Thus, each Class Loader is
responsible for maintaining and defining its own part of the namespace.

Linking and Record-Keeping

Because Java has separate namespaces into which classes can be loaded, it can't simply have a
unified "phone directory" tracking which class corresponds to which classname. Instead, the Java
Virtual Machine maintains a separate directory for each class. These independent directories keep
track of the names needed by each class.

For example, if class A has a reference to a class called B, the directory for A will have an entry for
B that points to the actual class the name represents. Figure 5.5 shows a more complicated example
with four classes referencing each other. A big applet could consist of more than four classes, but
the idea is the same: The applet is a set of classes that reference each other.

Figure 5.5 Four classes linked together.

Each box represents a class. The circled name at the top


of each box is the name of the class, and the entries
underneath show how the class's namespace is defined.

Attack of the Evil Class Loaders

The example described in Figure 5.5 shows reasonable, self-consistent namespaces. The Princeton
team discovered that a hostile class loader was capable of setting up a twisted namespace in which
different classes had different views of the Java environment. Such inconsistencies can be exploited
to create type confusion. A hostile class loader could launch a system-penetration attack.
Figure 5.6 shows an example of what an evil class loader can do. The figure shows two classes, A
and B, each of which refers to a classname "C". However, the two classes have different ideas of
what the name "C" means. Class A points to the class we've labeled C1, while B points to C2.

Figure 5.6 An inconsistent namespace, created by a


hostile class loader.

Classes A and B have different ideas about what C refers to.


This can sometimes be used to confuse the Class Loader.

Suppose that the Java code in class A allocates an object of type "C" and then passes that object to
class B. The Java byte code Verifier thinks everything is okay, since an object whose class was
named "C" is being passed into code that is expecting an object whose classname is "C". The
Verifier allows the operation to proceed. But when class B accesses the object named "C", the true
type will be C1, not the C2 that the Verifier approved. An object of type C1 is being treated as
though it were of class C2. This is type confusion.

When this attack is carried out, the evil class loader is asked twice to say which class corresponds
to the name "C". It gives back different answers: C1 for class A, and C2 for class B.

Taking Exception

The class-loader attack should have been impossible. Java's security rules prohibit applets from
creating class loaders. Unfortunately, the Princeton team discovered a flaw in the byte code Verifier
that allowed this rule to be violated. Nothing stops an applet from declaring a new class that is a
subclass of the ClassLoader superclass. It is up to the Security Manager to stop the actual
construction from occurring. In this case, the Security Manager check is bypassed because of a bug.
Read on for the gory details.

The rule against making class loaders is enforced by the object-oriented nature of Java. Every Java
class extends its superclass. Each class can be thought of as being a specialized version of its
superclass.3 Every class has one or more constructor functions, which properly initialize new
objects. Java requires each constructor to call the constructor of its superclass, or another
constructor of the same class, before it does anything else. For example, if you create a class called
MyHashtable that extends the built-in class java.util.Hashtable, then you have to provide a
constructor for MyHashtable. That constructor must call the constructor of java.util.Hashtable
before it does anything else. The byte code Verifier ensures that these rules are followed.

To prevent applets from making class loaders, the constructor for the class ClassLoader consults
the Security Manager, which generates a Security Exception if the class loader being constructed
would belong to an applet. This Security Exception can abort the creation of such an object. If an
applet defines a new EvilClassLoader class to extend the basic ClassLoader, then the new
constructor is required to call Java's basic ClassLoader constructor. Doing so generates a Security
Exception that prevents the applet from creating an EvilClassLoader.

What the Princeton team discovered was a trick by which a constructor could avoid calling its
superclass constructor, without being caught by the Verifier. This allowed them to create an
EvilClassLoader whose constructor did not call the basic ClassLoader constructor, and thus was not
subject to the normal Security Manager check. The EvilClassLoader could then create type
confusion.

Having created type confusion, the attacker could then exploit it to achieve full system intrusion;
that is, the attacker could do anything at all on the victim's machine.

The (Sort of) Fix

Sun Microsystems and Netscape had two options for fixing this problem. They could prevent the
superclass-constructor-avoidance by fixing the Verifier, or they could find another way of forcing
the basic ClassLoader constructor to be called. They chose to do the latter. They added an
initialized data field to every class loader, and set the field to true only when the basic ClassLoader
constructor was run. The basic ClassLoader would refuse to perform the crucial defineClass action
unless the initialized field was true.

The implementation created a new private ClassLoader method called defineClass0. This does the
real work of defineClass. Redefining defineClass to check the initialized flag and call defineClass0
only if the flag was true helps to block this particular security hole. The change does not prevent an
attacker from making a class loader, but it does prevent an attacker from using the new class loader
once it has been made.

The change took effect in Netscape Navigator 2.02. Unfortunately, future attacks managed to
circumvent this fix.

The Reaction

This flaw received more press coverage than any of the others. It had more news interest than the
DNS bug because it was more serious. Later bugs did not receive as much coverage because by the
time they came to light, the novelty of bug discovery had worn off. That does not mean that the
current and future security problems are not just as serious. Whether or not security problems are
splashed on the front pages, they still need to be taken seriously.

Perhaps the press coverage partly reflected a backlash against the extremely positive hype
surrounding most press stories about Java at the time. Java is great, but many of the exaggerated
claims went much too far. There was even a story stating that if you wrote programs in Java you
would never have to debug them because they would always be right the first time. To be fair, only
a little of the hype came from Sun. Much of it came from freelance consultants, self-proclaimed
experts, and trainers who had an interest in seeing their Java bandwagon become a juggernaut.

When the Applets Running Wild flaw was discovered, Sun Microsystems, Netscape, and the flaw's
discoverers gained some valuable experience discussing these issues with each other and with the
press. As a result, the parties did a better job of conveying simple and consistent information to the
public. Hopefully, this will remain true when future security holes come to light.

CHAPTER SECTION:9

Section 9 -- Casting Caution to the Wind


Software consultant Tom Cargill has discovered two security flaws related to the way in which
Java handles interface types. Both flaws involve a rare case in which Java fails to check whether a
method is private. Both also use type-casting operations on Java's interface types. By exploiting
these flaws, an attacker can call private methods normally prohibited by Java's security rules. Since
some of the security-critical values inside the Java system are protected by private methods, a
complete security breach using this attack is possible.

Simple Interface Casting

The core of Cargill's first discovery is shown in the following code:

interface Inter {
void f();
}
class Secure implements Inter {
private void f();
}
class Dummy extends Secure implements Inter {
public void f();
Dummy() {
Secure s = new Secure();
Inter i = (Inter) s;
i.f(); // should be illegal
}
}
This code allows the private f method of class Secure to be called illegally. The Java interpreter
fails to determine if f is private when i.f() is called.

The Princeton team figured out how to use this flaw to achieve full system penetration. This was
done by exploiting the fix to the class loader bug. The class loader bug was fixed by splitting the
critical defineClass method into a private method and a public method. The private method,
defineClass0, did the work. The public method checked the initialized flag and called defineClass0
only if the flag was true. Since the private defineClass0 method couldn't be called directly by an
applet, this was supposed to fix the class loader bug.

Unfortunately, a variant of the interface-casting trick shown here allows an applet to call the private
defineClass0 method directly, bypassing the check. This meant that the attack could create a class
loader by exploiting the Verifier bug. The initialized flag would be false, but that wouldn't matter.
A programmer could bypass the flag-check by exploiting the interface-casting trick to call the
private defineClass0 method directly.

By using this trick, an attacker could gain full system penetration under Netscape Navigator 2.02.

The Full Fix

Netscape fixed this problem in two ways. First, it fixed the flaw in its Java Virtual Machine that
allowed the interface-casting trick to work. Second, Netscape began storing and checking the
initialized flag inside the Java Virtual Machine, rather than in programmer-generated Java code.
Netscape eliminated the dangerous defineClass0 operation by integrating everything into the VM's
implementation of defineClass. This change took effect in Navigator 3.0beta3.

In reaction to the interface-casting bug, Netscape changed its Java implementation to protect itself
more generally against an attacker who had the ability to call private methods. By going beyond a
simple bug fix to improve the structure of the system, Netscape practiced good security
engineering. Its decision paid off when the next bug was discovered.

Advanced Interface Casting

Here is the core of Cargill's second discovery:

interface Inter {
void f();
}

class Secure implements Inter {


private void f();
}

class Dummy implements Inter {


public void f();

static void attack() {


Inter inter[2] = {new Dummy(), new Secure() };
for(int j=0; j<2; ++j) inter[j].f();
}
}

The first call, inter[0].f(), is legal since Dummy's f method is public. The next time around the loop,
inter[1].f() is illegal since Secure's f method is private.

In this case, Java was too smart for its own good. In order to improve performance, it only checked
for legality the first time through the loop. Theoretically, what was legal the first time would be
legal the next time. (See Chapter 2.) Though this is often a correct assumption, it broke down for
the code just shown.

This trick allows an attacker to call private methods in violation of Java's security rules. Had
Netscape not improved the structure of their system after the previous bug was reported, this bug
would have once again allowed the class-loader attack to work. However, because Netscape had
protected their system against private method attacks, this flaw was not easy to exploit.

CHAPTER SECTION:10

Section 10 -- Tag-Team Applets


The next group of attacks combines the two previous attack methods. By setting up two separate
naming environments and passing objects between them, this new group of attacks causes type
confusion, leading to security breaches.

These attacks are launched by putting two applets on a Web page and having the two applets
cooperate. Typically, both applets would be written by the same programmer, and located on the
same Web server.

To better understand this threat, the next section will examine in further detail how Java manages
namespaces. (See also Chapter 2.)

What's in a Name?

In a Java-enabled browser, several applets might be running at the same time. Since each applet
contains several classes, each with a distinct name, there's a danger that two applets might
accidentally use the same name. To prevent this, the Java designers decreed there should be a
separate namespace for each applet; that is, each applet should have its own view of which names
correspond to which classes.

Despite the fact that each applet has its own namespace, there are ways for two applets to pass
objects back and forth among themselves. One such channel involves public variables in the Java
runtime. Another channel is through manipulating threads. The result is that an applet can
potentially have an object whose name is defined in another namespace.

Things can get tricky when this happens. For example, suppose that the AncientGreek applet and
the Simpsons applet are running at the same time. Each of the two applets has defined a class called
Homer, but they have very different ideas of how Homer should behave. Worse yet, imagine that
the two applets communicate, and the AncientGreek applet ends up with a Homer Simpson object.
If the AncientGreek applet asks the object what its class is, the object responds, Homer. The
AncientGreek applet then asks Homer to recite an epic poem. Depending on your point of view, the
result would be either tragic or comical. In any case, it wouldn't be what the programmers wanted.

This isn't really a security risk; however, it becomes one if the Java system itself gets confused. The
danger is that the system will decide that two unlike types are really the same, when all that is the
same is their names. This sort of mix-up would constitute type confusion, and the applet could
break the Java type system, leading to a security breach.

One way that an applet could do this is by engaging in type punning. The applet would set up two
types with the same name, create an object of one type, and then use it as though it were an object
of the other type. For example, suppose that this is a Java type:

class Secure {
private Object secretData;
}

Another applet could create another type with the same name:

class Secure { // impostor


public Object secretData;
}

Now if the applet could get an object of the real Secure class and convince Java that the object
belonged to the impostor Secure class, Java would allow the applet to access the supposedly private
secretData field. As far as Java is concerned, this would be fine. The impostor secretData field is
public. The applet would have accessed data it was not supposed to see.

Type punning works for method calls, too. If the secretData field were replaced with a
dangerousOperation method, then an applet that could do type punning could call the
dangerousOperation method even though it was supposed to be private. In summary, if type
punning is possible, Java's security collapses.

Java prevents type punning by being very careful when deciding whether two classes are the same.
Rather than using the classname to make this decision, Java considers two classes the same only if
they have the same name and were defined in the same namespace (that is, by the same Class
Loader). That is sufficient protection to avoid type punning.

What Went Wrong: A Name Alone

Unfortunately, the creators of Java were not always so careful. In Java 1.0.2, Netscape 2.02, and the
first beta version of Internet Explorer, the types of interfaces and exceptions were compared by
name rather than by (name, namespace) pair as required. This led to a set of attacks that could
break Java's security system, achieving full system penetration. The attacks worked as described
earlier. The attacker wrote two applets defining different classes with the same name C. One applet
would create an object of class C and pass it to the other applet, which would operate on its C. This
leads to a classic type-confusion situation, which can be exploited by methods seen several times in
this chapter.

CHAPTER SECTION:11

Section 11 -- Big Attacks Come in Small Packages


We now describe an attack applet that exploits a Java security weakness in beta versions of
Microsoft's Internet Explorer 3.0. The weakness allows code in an untrusted applet to pass itself off
as part of a Java package. This flaw allows an attacker to gain full access to the victim's files and to
the network.

Understanding this flaw requires a more detailed explanation of Java packages.

Java Packages

The Java language supports the concept of packages-groups of Java classes meant to be used
together. Packages have names like java.lang and EDU.princeton.cs.sip. Every Java class belongs
to some package.

Packages serve two purposes. First, since the full name of a class is the package name followed by
the classname, they provide a way for different people to name their classes without having
accidental name collisions. For example, if we put all of our classes into the authors package and
you put all of your classes into the readers package, then our classnames cannot collide with yours.

The second purpose of packages is to restrict access to certain Java variables. When declaring a
variable, a programmer states which classes are allowed to access the variable. If the variable is
declared private, it is accessible only by the class that created it. If a variable is declared protected,
it is accessible only by the creating class and its subclasses. If a variable is declared public, it is
accessible by all classes. If a variable is declared neither private, protected, nor public, then it is
accessible only by classes in the creating class's package.

Some packages limit their membership to only built-in browser classes. Membership is restricted
by having the Virtual Machine ask the Security Manager to pass judgment on every request to join
a package. The Security Manager enforces restrictions by prohibiting classes loaded across the Net
from joining restricted packages. (For more information on this topic, see Chapter 2.)

What Went Wrong

In Microsoft's browser, there was an error in the way the Security Manager made its decisions
about package membership. Because of the bug, the Security Manager incorrectly used only the
first component of the package name to check access permission. This method failed for packages
whose names started with com.ms. Interestingly, several of Microsoft's built-in packages started
this way. (This is interesting because the domain name ms.com belongs to Morgan Stanley, not to
Microsoft. The package should have been called com.microsoft, although this change would not
have prevented the bug.)

The result was that untrusted applet code could join a sensitive package, and gain access to any
variables in that class that were accessible package-wide. These variables included, among other
things, the Security Manager's list of files to which the applet had access. A mischievous applet
could access any file on the system by changing the Security Manager's list to include the desired
file.

The Reaction

This flaw was found just before Microsoft was to ship the first nonbeta version of their Internet
Explorer 3.0 browser. Since the ship date had been announced (and a big public release hoopla was
planned), the ship date could not be moved. Since the product had a serious security flaw, shipping
it was not an acceptable alternative. Microsoft's development team launched a heroic effort to fix
the bug, test the solution, and restart the product release cycle in time to meet the original release
deadline. They succeeded.

The Big Attacks Come in Small Packages bug was the last major flaw found in the JDK 1.0.2
security model implementations. Following discovery of this attack applet, several months went by
before JDK 1.1 was released. As we discussed earlier, the period of six months without a major
security hole was of long enough duration to convince some people (in particular, Java's most
ardent supporters) to declare that all holes had been discovered and that Java had been rendered
secure through a series of patches. In fact, the previous version of this book was waved around as a
prime example of antiquated old news that no longer applied.

Even if another hole is never discovered in Java (unlikely, but why not dream big?), the
information contained in this chapter is important to people charged with designing, managing, and
using software in a security-critical situation. We all do well to learn from our mistakes as history
often tends to repeat itself.

CHAPTER SECTION:12

Section 12 -- Steal This IP Number


In a February 1997 post to comp.lang.java.security and Bugtraq, two British consultant/hackers,
Major Malfunction (probably not his real name) and Ben Laurie, followed through with their
decision to inform the unsuspecting of a couple of new Java-based attacks. Although the pair (and
assorted friends) had actually used Java to hack their way through firewalls a month earlier, they
thought it only fair to provide Netscape and Microsoft with ample time to address the problems
before they went public.

The most interesting thing about the two attacks was the fact that one of them (which we call the
Steal This IP Number attack) was found only in Netscape, and the other (which we call Cache
Cramming) was found only in Internet Explorer. This is interesting because it emphasizes the fact
that there are multiple Java implementations, and they are not always equivalent. To be sure, they
are all supposed to follow the same specification, but there are many ways to do so. As a result,
Netscape's model (which is very close to the JDK implementation) and Microsoft's model differ
significantly. This implies that as Java implementations diverge, holes related to implementations
will not be cross-platform. It also implies that some implementations will be more secure than
others.

It is stretching a bit to call the first problem a Java security hole, but because of the way the hole
was treated by the community we decided to include a description here anyway.

The Steal This IP Address hole, which exists in both Navigator 3.x and 4.0, allows a miscreant to
gain access to information from the client machine that would normally be considered secure. The
attack works as advertised on the Java/MSIE/Netscape Cache Exploit page
(seewww.alcrypto.co.uk/java/), but there is really nothing new to this discovery. On their page,
Major Malfunction and Ben Laurie say:

All we can do is log the real identity of a client machine, despite most precautions they might take
to prevent us from doing so . . . Devices such as firewalls, proxies, SOCKS hosts, etc., all succumb
easily to the call of the Java siren . . . Even the mighty anonymizer retires after the first round, nose
bleeding and ego bruised. To complete this recipe, we take one call to InetAddress.getLocalHost(),
mix it with a call to AppletContext.showDocument() . . .
And there you have it. Because the applet is running on the client machine and it is allowed to call
InetAddress.getLocalHost(), it can find out the client machine's IP. Although this may surprise
some users (especially those using the anonymizer, a proxy meant to protect client browser
identities from snooping Web servers), the ferreting out of this information is not really a
dangerous new invasion of privacy. The Web is not a private place, and this demonstration only
serves to bring that point home. Your browser is probably a blabbermouth. It is a clever move to
use Java to look up an IP at the client end through several proxy layers, but not all that clever.

Déjà Vu All Over Again

The flaw that appeared in the Netscape browser was actually a flaw that had been discovered and
fixed internally by JavaSoft some 10 months before it reappeared. In Sun's JDK 1.0.2 reference
implementation of java.net, an applet that calls getLocalHost() does not get a true hostname/IP
address; instead, it gets the loopback host ("localhost/127.0.0.1"). For some reason, the change
made to fix the privacy hole was not incorporated into Netscape. The fix was exceptionally easy.

CHAPTER SECTION:13

Section 13 -- Cache Cramming


The second of the two attacks discovered by Major Malfunction and Ben Laurie in February 1997
works against Microsoft's Internet Explorer browser, but not Netscape. Unlike the minor Steal This
IP Address problem, this problem is much more disturbing. On their Web
page,www.alcrypto.co.uk/java/, Major Malfunction and Ben Laurie claim "this loophole allows an
attacker to connect to any TCP/IP port on the client's machine." That's a bit of an overstatement,
but interesting information about listening ports can be gathered (for possible later use), which may
leave a firewalled host more susceptible to standard TCP/IP-based attacks. And that's bad news.

The Java Security Manager usually disallows port-scanning behavior, but the crackers use the well-
known trick of sticking some Java code (in this case, a port scanner) in the browser's cache and
later executing it through a file: URL (using frames in the usual way). This attack works because
Microsoft's cache layout is transparent.

This is an interesting variation on the Slash and Burn attack described on page 153. The attackers
cheat a bit for demonstration purposes by having the patsy clear his or her cache, but even without
this exercise, guessing the cache location (one of four possibilities) would not be all that much of a
challenge.

Contrary to their claim, however, Java security rules are no longer relaxed for code loaded out of
the cache (unless the cache happens to be in the CLASSPATH, which is not recommended). That
problem was fixed in the upgrade to JDK 1.0.2. (Yet another reason that the Slash and Burn attack
is obsolete.)
In any case, Microsoft apparently places HTML and class files in the same directory stored with
their original names (remember, a Java class will only run if it is correctly named). Although MSIE
can't browse cache files directly, HTML pages can reference cache files by explicit name. Thus, the
file: URL, if properly constructed, can invoke the Java class.

The applet stuffed in your cache is a port scanner. The port-scanning attack works because an
applet is allowed to open a socket connection back to where it came from. And guess where it came
from: Yep, the client machine.

So a port scan is carried out by their cache-bomb applet. Unlike the Steal This IP Address problem,
port scanning is very serious. Using this attack, a cracker might be able to discover things like weak
sendmails listening on port 25, leaving only the problem of getting the port-scan information back
to the cracker site. Accomplished crackers can simply use the URL-lookup covert channel to do
this. Unfortunately, this approach is only one of many ways of sending interesting tidbits out from
an applet.

Resolution

The information released by Major Malfunction and Ben Laurie prompted Microsoft to release a
patch to solve the problem.

CHAPTER SECTION:14

Section 14 -- Virtual Voodoo


In March 1997, Sun announced the discovery and eradication of a bug in the Verifier of the JDK.
The bug was present in all Java VMs, and Sun shipped a patch to Java licensees. Sun claimed that
the bug was discovered by the engineering team during a standard security audit and was fixed
within 24 hours of discovery.

No attack based on this bug was ever devised. In fact, very little information about the fix was
disseminated publicly. Statements made by Sun to the press emphasized the complexity of an
exploit. Realistically, it sounds like the problem was similar to the You're Not My Type problem-an
attacker would need to create malicious byte code to exploit the problem.

Preemptive Strike?

We found it a bit peculiar that Sun announced the discovery of a flaw in the Verifier and the
dissemination of a patch to vendors. We speculate that someone outside of Sun had discovered the
problem and Sun decided to announce the flaw before the discoverer did.
CHAPTER SECTION:15

Section 15 -- The Magic Coat


Chapter 3 serves to emphasize that code signing is complicated. As in the original sandbox model,
there is plenty of room for error in designing and implementing a code-signing system. The Magic
Coat hole was a fairly straightforward problem in the implementation Java's Class class and was
quickly explained on both the Princeton Web site (www.cs.princeton.edu/sip) and JavaSoft's
security site (java.sun.com/sfaq). The problem was that the method Class.getsigners() returned a
mutable array of all principals that signed a particular class. It was possible for an applet to modify
the array, label itself as signed by a trusted principal, and thus gain whatever privileges it wanted.
The applet didn't even have to guess which principals were trusted: the system helpfully gave it a
list of all known principals "t" and the applet could "try on" the known identities one by one until it
found a highly privileged one. The fix was as simple as returning only a copy of the array and not
the array itself.

Consider a situation in which Alice has been granted no security privilege on a Web user's system.
In fact, contrary to what the original JavaSoft statement about the bug claimed, Alice can be
completely unknown to the system. In other words, code signed by Alice is not trusted any more
than the usual applet off the street. If the Web user (using the HotJava browser, which at the time
was the only commercial product that supported JDK 1.1.1) loads an applet signed by Alice, that
applet can step out of the sandbox by exploiting the hole.

The fact that the system need not have Alice's public key in its database is important. It means that
Alice can be any arbitrary attacker who knows how to sign an applet with a completely random
identity. Creating such an identity is easy, as is signing an applet with that identity. This makes the
hole very serious indeed.

The hole allows Alice's attack applet to change the system's idea of who signed it. This is especially
bad if Alice is not granted privilege to run outside the sandbox, but Bob is. Alice's applet can
exploit the hole to change its level of permission to include all of Bob's privilege. Alice's applet can
get the maximum amount of available privilege doled out to any signer known to the system.

If you liken the signature/privilege identities to coats in a closet, Alice's attack applet can try on
each coat and attempt various disallowed things until it discovers which of the coats are "magic"
and allow it to gain privilege. If a magic coat is discovered, Alice's applet can step out of the
sandbox and do things it should not be allowed to do. Trying on coats is as simple as attempting a
disallowed call and watching to see what happens.

In brief, by exploiting this hole, the Magic Coat attack applet can get a list of all signers known to
the local system, determine which if any of those signers is trusted, and then relabel itself so it
appears to have been signed by a trusted signer. The result is that the applet can completely evade
Java's security mechanisms.

JavaSoft fixed the flaw in release 1.1.2 of the JDK. Soon after discovery of the hole, a patch was
created and sent to the various Java licensees. Since neither Netscape nor Microsoft supported JDK
1.1 code signing when the hole was discovered, they were not vulnerable. In fact, as Chapter 3
discusses, both Netscape Communicator and MSIE have different models of code signing than Sun
does.

Assessing the Bug

JavaSoft states correctly that this bug "represents minimal exposure to users" since it affects neither
of the popular browsers. It is important to note that HotJava is completely susceptible to this
problem unless code signing is entirely disabled. HotJava has always been suspect from a security
perspective, and the Magic Coat hole shows why.

So was this bug serious? Yes. It provides yet another example of how complicated systems can
break down in unexpected ways. Dr. Peter Neumann, moderator of comp.risks and noted security
expert said at the time, "This is another instance of an old RISKS story-a surprisingly large portion
of the entire infrastructure must be trustworthy, including pieces you might not have realized were
critical. That statement is perhaps best thought of as a corollary to Leslie Lamport's classic
statement, 'A distributed system is one in which the failure of a computer you didn't even know
existed can render your own computer unusable.'"

CHAPTER SECTION:16

Section 16 -- Verifying the Verifier


As Chapter 2 describes, one of the essential pieces of the Java security model is the Verifier, which
is responsible for checking the integrity of a class file and its byte code. Each VM, including the
VM in your browser, has its own version of the Verifier. Thus the Verifier acts as a kind of
independent verification mechanism to double-check the integrity of mobile Java that arrives from
unknown parts.

The University of Washington's Kimera Project, led by Professor Brian Bershad, implemented a
clean-room Verifier as part of a larger mobile code architecture experiment. The main idea behind
the Kimera Project is to centralize Verification of byte code instead of having a Verifier running on
each client. Their aims in creating a Verifier were to make it small, simple, and able to work
independently. The discussion here is distilled from extensive Kimera Web documentation, which
can be found at kimera.cs.washington.edu.
After creating their Verifier, the Kimera Project team wanted to test it to make sure it performed in
a secure and robust manner. The question was, how to do this? The answer to that question led to
an empirical demonstration that the Kimera Verifier was safer than many commercial Verifiers
then in widespread use.

Most commercial Verifiers perform security checks in a series of stages that are distributed
throughout the VM architecture. In particular, Chapter 2 describes how verification is traditionally
broken into static and runtime processes. By contrast, the Kimera Verifier packages a complete
verification system as one single component. An added bonus to the Kimera Verifier is the
inclusion of a number of safety axioms distilled by the Kimera Project from the Java VM
specification. This has the advantage of allowing the Kimera Verifier to be checked for correctness
in a more thorough manner than the code for commercial Verifiers.

The Kimera Verifier aligns closely with class file verification as described in the VM specification.
That means the process of verification is logically divided into four stages (see Chapter 2 for more
on these stages):

• Verification of class file consistency and structural soundness


• Verification of instruction boundaries and safety of branches
• Data flow analysis that verifies that operands preserve type-safety rules
• Verification that the class, along with the rest of the VM, does not break global type-safety
rules or interface restrictions

In order to test their Verifier, the Kimera Project team created a mutation testing system. The end
product of the approach is a test suite that includes millions of tests. The tests, millions of legal and
illegal byte code patterns, were created by applying a set of simple mutation rules to a small
number of primitive test cases. Without access to an oracle (the lack of which is the traditional
Achilles' Heel of testing), there is no way to say whether behavior of the Verifier on any given test
is correct or incorrect. One way around this is to submit the tests to a number of different Verifiers
and see what happens.

According to this paradigm, whenever results from a number of distinct Verifiers disagree, further
investigation into the cause of the disagreement is warranted. The obvious problem with this
approach is that if all Verifiers perform equally incorrectly on a test case, nothing suspicious will
be observed, and all Verifiers will be incorrectly thought to have done the right thing. Regardless of
that serious flaw, the Kimera test suite was able to identify a large number of errors in commercial
Verifiers, which led to correction of the Kimera Verifier throughout its testing.

The Kimera testing paradigm has much in common with N-version programming, in which N
distinct versions of a piece of software are run simultaneously and a vote determines the final
result. (For more on N-version programming see [Voas and McGraw, 1998].) We believe Kimera's
testing methodology is a glimpse at the future of testing. Given the vast computational resources of
today, there is no reason not to automate testing to a much greater extent than was feasible just a
few years ago. Traditional metrics, such as test coverage, can be used to gauge the efficacy of such
automated testing.
Flaws in the Verifier

In April of 1997, the Kimera Project announced the discovery of 24 flaws in Sun's Verifier
implementations and 17 flaws in Microsoft's Internet Explorer. Because Netscape's Verifier is very
closely based on the one in Sun's JDK, it is safe to assume that most of the 24 flaws in Sun's
Verifier exist in Netscape's as well (in fact, testing confirmed this). As we detail next, the flaws can
be divided into several categories: security holes, weaknesses, and ambiguities.

Flaws in Sun's JDK


The Sun flaws were found in the Verifiers included in JDK 1.0.2 and JDK 1.1.1. JDK 1.1.1
contained only 15 of the 24 total flaws. One of the flaws was serious enough to warrant an
immediate patch to the JDK. We describe it now.

The most serious flaw was a type-safety error that allowed a number to be converted into a pointer
to any object in the VM. An attack applet based on this flaw could gain access to privileged
information or otherwise force an illegal operation. As we describe in Chapter 2, type-confusion
errors of the sort that this bug could lead to can be used to circumvent Java's security mechanisms
entirely. This flaw allowed applets to assign a long or a double into an object reference, which can
in turn be used to forge pointers inside the VM. Both JDK 1.0.2 and Netscape's Navigator Gold
3.01 included this bug.

Five other type-safety problems were also discovered in Sun's Verifier. One of these errors
prompted Sun's security patch. This flaw, which existed in JDK 1.1.1 and JDK 1.0.2, involved the
fact that a method can declare its maximum number of locals to be N, but take M arguments in its
signature, where M can be greater than N. A description of the type-safety problems can be found
atkimera.cs.washington.edu/flaws/sunflaws0423.html.

Besides the type-safety flaws, 11 access-flag flaws were identified. One example of such a flaw
found in JDK 1.1.1 and JDK 1.0.2 is a flaw in which access flags for some methods are
unrestricted. A properly constructed attack might be able to affect type safety and lead to a type-
confusion attack. Once again, more information is available on the Kimera Web site. Finally, seven
flaws can be classified as instruction or class file validity problems. The most serious of these
flaws, found in JDK 1.0.2, was a problem whereby the class namespace management system could
become confused by malformed classnames.

Flaws in Microsoft Internet Explorer


The Kimera Project's testing methodology uncovered 17 flaws in Internet Explorer versions 4.0 and
3.02. Microsoft released a patch to address some of the more serious problems. Ten of the flaws
that applied to the MSIE Verifier also occurred in Sun's Verifier.

The most serious flaw was the same flaw discovered in the JDK implementation: the ability to
assign a long or a double into an object reference. This flaw prompted the security patch. A list
briefly explaining of all the flaws can be found
at kimera.cs.washington.edu/flaws/msflaws0423.html.
The Reaction

One of the most interesting things about the Verifier bugs discovered by the Kimera group was the
way in which the news broke in the press. Somehow, Sun was able to stage a preemptive PR strike
before the Kimera announcement was even made. Their version of the story, which was later
changed to more closely align with reality, was covered in the Wall Street Journal. In particular, the
Sun announcement focused on only one of the many bugs.

The importance of type safety to the Java security model was also reemphasized by the Kimera
Project findings. Sun claimed that many problems in the type system might lead to a denial-of-
service attack through a crash, but did not acknowledge that it is often trivial to turn these crashes
into more insidious type-confusion attacks. The next Java security hole, also discovered by the
Kimera Project, served to bring that point home.

CHAPTER SECTION:17

Section 17 -- The Vacuum Bug


In June of 1997, the Kimera Project team uncovered another security hole in JDK 1.1.2 and the
HotJava browser. Like the previous 24 flaws discovered, the new security hole had at its core a
type-safety flaw in the Verifier. Prompted into further action by Sun's response to the first
announcement, the Kimera Project team decided to create a complete exploit based on the new
flaw. That was one way to emphasize that type-safety problems should always be taken seriously!

The flaw itself existed only in JDK 1.1.1 and JDK 1.1.2 and was never present in either of the
major browsers. By itself, the flaw manifests itself as a VM/browser crash (the browser being
HotJava). That is, an applet that passes the Verifier with flying colors would end up crashing the
VM when it actually ran. The crash is caused when the applet directs the browser to access
inaccessible memory.

Using a PC debugger (MSDEV), Kimera team members were able to determine which instruction
was causing the browser to crash. With this information in hand, it was possible to cause the
browser to jump to a legitimate address and start reading arbitrary memory ranges. In a strings-like
move, the malicious code directed an ASCII text version of the targeted memory range back to a
collection server.

The attack applet created by Kimera exploited the type-safety problem to carry out this attack. The
most pernicious aspect of the attack was the applet's ability to determine sensitive information
about the user from the browser's memory space, including configuration settings, private crypto
keys, name, email address, browser history, cache contents, etc.
Why the Flaw Was Exploited

Other than its ability to steal sensitive data, the main purpose of the attack applet was to
demonstrate that what Sun had called "less serious" type-safety problems could be easily turned
into full-fledged attacks. Java has at its heart a language-based security model. Such models depend
completely on the type system to provide security guarantees.

CHAPTER SECTION:18

Section 18 -- Look Over There


In August of 1997, Ben Mesander, a professional programmer and consultant from Creative
Concepts in Boulder, Colorado, uncovered a security hole that allowed untrusted Java applets the
ability to make network connections to arbitrary locations. This flaw affected browsers with VMs
coded to the JDK 1.0.2 specification. According to the JDK 1.0.2 security model, applets are not
allowed to make network connections to any host other than the Web server from which they come.
Three browsers-HotJava, Netscape, and MSIE-were all susceptible to the problem (at least with
some proxy configurations, as we explain later).

To demonstrate the flaw, which was most apparent in the MSIE implementation of Java, Mesander
wrote a simple applet that was able to load a graphic from www.microsoft.com even though the
applet was not hosted from that server. The ability to open arbitrary connections to other servers is
distressing for several reasons. The first is that such a connection provides a channel for indirect
attacks. That is, a bad guy can cause your browser to run an attack against another server, making
your machine take the blame. Another is that information from your machine may be siphoned off
to another site. A third is that some attack code could be fetched from another location, leaving the
true culprit applet in a less-suspicious situation should a successful attack be carried out against
your machine. Finally, and most realistically in terms of the example we will show later, the attack
applet is able to download an image from any Web server, even those servers behind firewalls. If
you have images on your intranet that should not be seen by people outside your organization (such
as sales data with sensitive pricing information), the seriousness of the attack is apparent. In any
case, it is clear that Mesander's applet was able to do something that should not be allowed.

The applet itself is shown as Listing 5.1. Mesander serves up this "test applet" as an example on his
Demo of Browser Security Hole page atneurosis.hungry.com/~ben/. The applet attempts to load a
graphic from a disallowed site (Microsoft's Web site). The applet is able to determine its level of
success and display a message relating to the security of your browser.

Listing 5.1 Ben Mesander's applet WhereDoYouWantToGoToday fetches an image from a


disallowed location.

// ben@gnu.ai.mit.edu
import java.applet.*;
import java.awt.*;
import java.net.*;
class WhereDoYouWantToGoToday extends Canvas
{
Image image = null;
WhereDoYouWantToGoToday (Image image)
{
super ();
this.image = image;
}

public void paint (Graphics g)


{
g.drawImage (image, 0, 0, this);
}

public Dimension preferredSize ()


{
return new Dimension (image.getWidth (this), image.getHeight (this));
}

public Dimension minimumSize ()


{
return preferredSize ();
}
}

public class example0 extends Applet


{
private Button b;
public void init ()
{
createUI ();
}
private void createUI ()
{
setLayout (new BorderLayout ());
b = new Button ("Go!");
add ("West", b);
}

public boolean action (Event evt, Object arg)


{
if (evt.target.equals (b))
{
try
{
Image i = getImage (new URL (getDocumentBase (),
"cgi-bin/redirect?where=" + URLencode
("www.microsoft.com/library/images/gifs/
homepage/tagline.gif")));
MediaTracker tracker = new MediaTracker (this);
tracker.addImage (i, 0);
tracker.waitForID (0);
if (i.getWidth (this) == -1)
add ("Center", new TextField ("Browser did not get exception,
but image did not load"));
else
{
add ("Center", new WhereDoYouWantToGoToday (i));
add ("East", new TextField ("Your browser is not secure."));
}
validate ();
}
catch (Exception e)
{
add ("Center", new TextField ("Exception while fetching image:"
+ e));
validate ();
}
return true;
}
return super.action (evt, arg);
}

private static String URLencode (String s) {


int i; StringBuffer b = new StringBuffer ();
for (i=0; i < s.length () ; ++i)
{
b.append (URLencode_helper ((char) (s.charAt (i) & 0xff)));
}
return b.toString ();
}

private static String URLencode_helper (char c)


{
if (c %lt; 16) {
return "%0" + Integer.toString (c, 16);
} else if ((c < 32) || (c > 127) || (" +&=%/~#".indexOf (c) >= 0)) {
return "%" + Integer.toString (c, 16);
} else {
return String.valueOf (c);
}
}
}

The applet works in concert with an HTTP redirect call that the browser being attacked follows to
wherever it leads. Most of the dirty work takes place in the line:

Image i = getImage (new URL (getDocumentBase (), "cgi-


bin/redirect?where=" + URLencode
("www.microsoft.com/ library/images/gifs/homepage/tagline.gif")));

Having your Web server implement the redirect command is very simple. With the Microsoft IIS
server, the code is as simple as:
Response.Redirect("http://" & Request.QueryString("where")).

This applet demonstrated flaws in the Java implementations of several browsers. In particular,
results before the browsers were patched showed the following vulnerabilities:

• MSIE browsers on all versions of Windows had a serious bug, which Microsoft took very
seriously and released a patch for within several days of the bug's discovery.
• Netscape browsers were susceptible only if they were set up to use an HTTP proxy server. In
particular, proxies set up to work outside the firewall caused big problems. Netscape fixed
the bug in later releases of their browser.
• The HotJava browser allowed classes to be loaded via redirects, but did not allow images to
be loaded this way.
It is important to emphasize that this is a very clear example of an implementation bug and not a
design problem. Java's design at the time was such that redirects were rendered impotent as an
attack mechanism. However, all the vendors (including Sun's HotJava team) did not properly
implement the design.

Another important point is that the attacker must know the URL of the image or class file that is
being indirectly fetched. Sometimes, getting this information is not possible. Other times,
standardized naming conventions constrain the space of possible names enough that a good guess
works. A good attack strategy would be to combine this exploit with a standard social engineering
attack that is used to determine the name of the image file or class file to steal.

Last, But Not Least

Mesander's redirect exploit was the last Java-related problem discovered in JDK 1.0.2 and JDK 1.1.
In fact, Java experienced a calm period of several months before the next serious hole was
discovered in July of 1998. The timing of these flaws supports the observations that discovery of
holes tends to be clustered around major releases and that eventually a majority of the holes are
found and fixed.

CHAPTER SECTION:19

Section 19 -- Beat the System


In July 1998, Dirk Balfanz, Drew Dean, Edward Felten, and Dan Wallach of Princeton's Secure
Internet Programming team found another Java security flaw that allows a malicious applet to
disable all security controls in Netscape Navigator 4.0x. After disabling the security controls, the
applet can do whatever it likes on the victim's machine, including arbitrarily reading, modifying, or
deleting files. As usual, the Princeton team implemented a demonstration applet that deletes a file.

The hole that the Princeton Team discovered was actually present in all VM's, but was only
exploitable in Netscape 4.0x. This serves to emphasize the difference between holes and exploits
discussed earlier in the chapter (see page 142). Figure 5.7 illustrates the relationship between a
security hole and an attack applet that exploits the hole.

Figure 5.7 Security holes and exploits.

An attack applet is created to exploit one or


more security holes in order to completely
compromise security.
This attack pieced together several security-related flaws. The first flaw, discovered by Mark
LaDue, is that the Netscape 4.0x Security Manager does not prevent untrusted applets from creating
subclasses of Netscape's AppletClassLoader class. This allows an applet to create a Class Loader.

The second flaw, discovered by the Princeton team, is that the class loader mechanism, as of JDK
1.2beta3 and Netscape 4.0x (and, apparently, Internet Explorer 4.01), sometimes allows a malicious
Class Loader to override the definitions of built-in classes. Specifically, a Class Loader can use its
defineClass() method to define classes that have the same name as any built-in class, provided that
the built-in class being overridden has not yet been loaded into the Class Loader's namespace.

Since the correct operation of the Java VM depends in part on the correct behavior of built-in
classes like java.lang.Class, java.lang.Object, and java.lang.Exception, a Class Loader that can
override the definitions of built-in classes can change the rules of the Java VM.

The third flaw is a subtle bug in the Verifier that opens a window of vulnerability for the class-
overriding trick. The details of this third flaw are a bit too sensitive to publish here. Suffice it to say
that the Verifier assumes that only certain types of objects can appear on the stack in some special
situations, and that class-overriding allows an attacker to violate this assumption.

The combination of these three flaws allows an attacker to create type confusion, which leads to a
full-blown security breach as usual.

Class Loading Take Four

One instructive thing about this attack is the role of class loaders. Several previous attacks have
exploited class loader problems, and this attack shows that the class loader design was still not
right, even after several iterations. Indeed, the problem of designing a correct class loader API is a
subject of research by Drew Dean at Princeton [Dean 1997], leading to a major portion of Dean's
Ph.D. thesis. The problems with class loaders are perhaps the best counter-argument to those who
say that Java's security problems have been superficial implementation bugs. Fixing these problems
is hard enough to merit a Ph.D. thesis!

This flaw is fixed in Netscape Navigator 4.5.

CHAPTER SECTION:20

Section 20 -- What These Problems Teach Us


The designers of Java tried to ensure that applets could not misbehave. Although they often claim
success (with apologies to Mr. Clemens) claims about Java's security have been greatly
exaggerated. Lately, the security message emanating from the major vendors has tended to
emphasize the idea of managing risks. That's good. We live with risks every day, and there is no
reason we can't live with mobile code risks, especially if we take on the risks with our eyes open
and some forethought about possible consequences.

All implementations of Java have had some rather serious security flaws. Even "mature"
implementations including Java 2 are not perfect. All known attacks have been detailed in this
chapter. So the question is what to do as we face these risks. Turning Java off is certainly one
solution, but not a very satisfying one. Java has lots to offer, and not using it would probably be a
poor decision in the long run. The new Java 2 security model, with its emphasis on policy, may
help make Java risks easier to manage. The trick is setting up a sound policy that makes sense for
your organization.

Each of the security problems that we discussed in this chapter can be implemented as an attack
applet. In fact, the Princeton team regularly creates attack applets in the lab to test the limits of
Java's vulnerabilities.4 Although rumor has it that attack applets based on the DNS bug and the
Princeton Class Loader attack have appeared on underground Web sites, there is no convincing
evidence that such attack applets have ever been used to crack a system on the Net. Nonetheless,
the main reason attack applets need to be taken very seriously is that the end result of a successful
attack is full system penetration; in other words, attack applets are capable of aiding a cracker in
taking over your machine. Once you no longer "own" your machine, a cracker can install a virus,
erase your hard disk, place Trojan Horses and logic bombs, or maybe just spy on you and steal your
credit card information, bank account number, business plans, or personal correspondence.

Java brings the formidable power of mobile code to the Web. Trading security for this power is a
tough pill to swallow, but a pill that's probably worth ingesting. Users want audio/visual
conferencing without cross-network eavesdropping. Users want loosely coupled computation for
things like factoring without cycle theft and denial of service. Users want games without Trojan
Horses. Users want save and restore for applet-based preferences without having their valuable files
stolen. Being security conscious, users can probably have what they want, without getting anything
that they don't need.

Securing Java:
Improvements, Solutions, and
Snake Oil
Now that we've covered hostile applets, let's turn to the positive side and talk about what can be
done to improve Java's security. Of course, security can always be improved by fixing specific
bugs, as Sun Microsystems, Netscape, and Microsoft have been doing. Again, removing a needle
from a haystack is easy once you've been stuck by it. This chapter focuses on more global issues
surrounding the design of Java. What sorts of high-level antidotes are there to some of Java's
security concerns?

This chapter has two major goals. The first goal is to discuss high-level concerns about Java and to
make some suggestions about how they could be addressed. Many of these suggestions carry over
from the original edition of this book. Since 1996, only a handful of our original suggestions have
been adopted. At the risk of being redundant, we raise them again. The high-level concerns include
programming language issues, formal analysis, applet logging, trust models, the distributed nature
of the security model, implementation versus specification, decompilation, trusted dialogs, and
policy management. Fixing the way that Java does some of these things will certainly improve
security. The second goal is to discuss a number of Java security add-on products that have
appeared on the market. A number of companies have introduced add-on products that are meant to
improve Java security by mitigating known risks. Vendors include Finjan, Digitivity (now Citrix),
Security7, and others. We take a quick look at each of these products and consider their goals in
light of objective reality.

Chapter Six Sections


1. Improving the Platform
2. Writing Safer Code: A Defensive Stance
3. Third-Party Solutions or Snake Oil?
4. Risks That Third-Party Vendors Can Address
5. Risks That Third-Party Vendors Can't Address
6. Assess Your Risks

Securing Java: Improvements, Solutions, and Snake


Oil
CHAPTER SECTION:1

Section 1 -- Improving the Platform


Most problems discussed in Chapter 4, "Malicious Applets: Avoiding a Common Nuisance," and
Chapter 5, "Attack Applets: Exploiting Holes in the Security Model," involve specific
vulnerabilities that have more to do with the current implementation of Java than with the Java
security model itself. There are, however, some general concerns raised by the computer security
community regarding Java. Many of these concerns are not new; in fact, we wrote about a majority
of these issues in 1996. We discuss what progress has been made since then (if any). This section
discusses some of these concerns and how addressing them would improve Java security.
Language Issues

The first group of issues has to do with the design of Java itself. There are a handful of main
language issues to discuss. Note that these are criticisms of the language itself and not criticisms of
the current implementation. It may be too late to address these concerns now that the Java ball has
been rolling for a couple of years, but these issues still warrant discussion.

Public Variables
First among the language concerns is the fact that Java allows a kind of variable called a public
variable. These variables can be overwritten by a method from any Java class, no matter where the
class may have been defined or from where that class may have been loaded. Storing any data in a
public variable introduces a security risk.

Public variables are still writable across namespaces. This means that a public variable can be
overwritten by an applet that has come across the network. The global nature of public variables
opens an entire avenue of attacks.

Protected Variables
A similar concern is raised by protected variables and classes. The real problem is that the label
protected implies more security than it may actually offer. Protected variables and classes can be
accessed by the class that created them, the creator's subclasses, and classes in the same Java
package. Packages are a bit peculiar in Java (something we discuss later in this chapter). The result
is that code can declare itself part of a package and gain access to protected variables. Developers
should be aware of this risk and use protected variables sparingly.

Packages
A third language issue involves Java's package mechanism. Basically, packages in Java are too
weak. A variable or method can be declared as accessible to classes within the current package, but
there is no alternative way to control what sorts of other classes can access the variable. It would be
better to have more explicit control over who can access variables. The flexibility to choose two of
these classes, those four, and one from that other package to make up a new package would make
the modularity mechanism much more versatile.

Consider the following: The java.io.File class is dangerous, and untrusted applets have no business
accessing it. However, the same File class is required by code in java.lang.ClassLoader in order for
the Class Loader to load classes from the local disk. Since java.io.File is needed outside its
package, it must be declared public, making it accessible to applets. But making it public introduces
a serious security hole. The hole can be plugged by adding a few rules (some code) to the Security
Manager (or the Access Controller, as the case may be). As these parts are built by the browser
vendor (or some other Java application writer), such a solution is generally not very reasonable.

It would be better to have some way for java.io.File to be accessible to the java.io and java.lang
packages, but not to any other code. Doing this would create a stronger package system in Java.
In addition, the way membership in a package is declared is somewhat strange in Java. Most
languages with package-like modularity use a single file for each module, outlining which code is
in the module and who is allowed to access the module. The owner of a module can then easily
control who is allowed to use code and variables in the module. In Java, there is no single
declaration of a module or a list of members having access to the module. Instead, each class itself
declares which package it belongs to. That means that an external mechanism (such as the Security
Manager or the Access Controller) must decide whether partially trusted code should be allowed to
declare itself a member of a particular package. Because the package system is more complex than
it needs to be, there is more room for error than with a more typical setup.

Byte Code Representation


The next programming language critique is more abstract: Java's byte code specification is not
optimal. As an intermediate representation between the Java source and the machine code of the
platform on which Java runs, byte code plays an important role. We believe there are better ways to
represent the same sort of platform-independent code.

One construct, called Abstract Syntax Trees (AST), would be easier to type check than existing
Java byte code. ASTs would greatly simplify global dataflow analysis, which would speed up the
Verifier and reduce the odds of a Verifier bug. That's because the current Verifier must
painstakingly deduce information that ASTs have built directly into them. ASTs also have the same
semantics as the source languages they represent. That means there is no need to question whether
the intermediate representation is more powerful than the source language. By contrast, Java byte
code semantics are different from Java source code semantics. Who can guarantee that Java byte
code is constrained in similar ways to the Java language itself?

If you're at a loss imagining why that matters, consider that some aspects of Java security depend
on Java's semantics and not on byte code semantics. Does that mean it may be possible to do things
directly in byte code that a Java compiler would, for security reasons (or other good reasons), not
allow? Unfortunately, the answer is yes. For details on this issue, see page 196. In any event, since
ASTs have a compilation speed (source to AST) comparable to byte code compilation speed
(source to byte code), why not use ASTs?

These and other language issues are discussed in greater detail in the Secure Internet Programming
team's early paper, "Java Security: From HotJava to Netscape and Beyond" [Dean et al., 1996]. If
you are interested in learning more about such things, the article is available on the Web
atwww.cs.princeton.edu/sip/pub/secure96.html.

Dynamic Class Loading


Class loading has always been a problematic issue for Java. In fact, even though class loading has
been redesigned and supposedly fixed in every successive JDK, each implementation has included
at least one serious security flaw. A recent Java security hole, discovered in a beta version of Java 2
in 1998, was yet another problem with class loading (see Chapter 5 for details).
In Chapter 2, "The Base Java Security Model: The Original Applet Sandbox," where class loaders
are introduced and discussed in some detail, we point out that there are really two functions
performed by class loaders:

1. locating and fetching byte code


2. managing namespaces.

There is no reason these two capabilities need to be combined into a single class. In fact, some of
Java's more serious security holes could have been avoided if class-loading architecture had
initially separated the two functions. The culprit in many security problems has been in defining the
namespaces seen by different classes and how the namespaces relate to each other.

As the approach to class loading has changed throughout Java's short life, class loading has mutated
from a completely extensible architecture (which was dangerous from a security perspective), to a
system in which only trusted code could create a Class Loader, and back to a system in which
untrusted code might once again be able to safely create a Class Loader (that is, if it follows a
stringent set of rules). If you decide to create a Class Loader of your own, it is best to change only
those aspects of class loading related to locating and fetching byte code. Avoid changing the
namespace structure if at all possible.

For more on the complications of class loading and how to fix them, see Drew Dean's doctoral
thesis [Dean, 1998]. As we said before, class loading is a perfect example with which to counter
claims that Java's security problems are all related to superficial implementation bugs.

Can Java Be Proven Correct?

Our previous discussion of ASTs and the current byte code representation leads directly to the next
topic: formal verification. That's because any questions of provability are compounded by having
two languages with separate semantics to understand (Java source code and Java byte code).
Formal verification involves proving, in much the same way that a theorem is proven in
mathematics, that a program does what it is supposed to do or that a programming language has
certain properties such as type safety. This is a laborious process, to say the least.

There are many sorts of formal analysis Java could undergo. The security model itself (if
formalized) could be analyzed. The Java source language could be formalized in a specification,
then shown to be valid. The same thing could be done for Java byte code. In addition, a better-
specified formal relationship between Java byte code and Java source code could be worked out.
The Java VM could also be formally verified.

Computational Logic, Inc. (CLI), Schlumberger, and JavaSoft collaborated in 1997 to create a
formal model of a portion of the JVM. The model was built in Common LISP and provided some
formal analysis capabilities. The model performs extensive runtime type-safety checks, something
that the standard VM does not do (the standard VM relies on the Verifier to perform many type-
safety checks instead). CLI focused on Card Java (see Chapter 8, "Java Card Security: How Smart
Cards and Java Mix"). It appears that CLI is not planning further formal analysis work. For more
information see: www.cli.com/software/djvm/index.html.

Formalizing the Security Model


To this day, Java still has no formal security model. The complete security policy has never been
specified at a sufficiently high level for current versions of the language. As a group of security
researchers once said, "A program that has not been specified cannot be incorrect; it can only be
surprising" [Young, et al., 1995]. It is not possible to determine just what secure means without a
creating a formalized policy. Furthermore, a particular implementation of a nonexistent policy
cannot be properly verified.

Some progress was made toward this goal in a report commissioned by Sun back in 1996. The
report, entitled Security Reference Model for JDK 1.0.2, explained (in informal English) Java's
implicit security policy (at least for the base security sandbox described in Chapter 2) [Erdos, et al.,
1996]. The SRM is available through www.javasoft.com/security/SRM.html. Creating the SRM
was a useful exercise; unfortunately, any utility provided by the SRM was caught in the Internet-
time cross fire. The SRM is completely out of date. Given the rigorous security demands of
electronic commerce, documents like the SRM should be demanded by organizations using
consumerware in their security-critical systems.

Progress on the formalization front has also been made by programming language researchers (see,
for example, [Drossopoulou and Eisenbach, 1998; Stata and Abadi, 1998]). Work on the soundness
of Java continues.

Analyzing Java Source


The Java source language is powerful and includes a whole host of features. Only recently has any
sort of specification of the language appeared. Given a complete specification of Java source
semantics, a formal analysis can be completed. This work is currently under way.

Analyzing Byte Code


Java byte code plays a critical role in the way Java works. Some progress has been made with
regard to formalizing byte code semantics with the release of a specification for the VM [Sun
Microsystems, 1996b] (also see [Venners, 1998]). Given a sufficiently detailed specification, it is
possible to begin work on proving that the VM and Verifier systems are implemented properly.
Preliminary work on testing Verifier implementations has been done by the Kimera Project at the
University of Washington (for more on the Kimera effort, see Verifying the Verifier in Chapter 5).

Comparing Byte Code and Java Source


Showing how Java byte code behaves in relation to Java source code was impossible without a
semantics for both. Now that we have two specifications, we can determine whether or not byte
code is more powerful than Java source code. Are there things that you can do with byte code that
you can't do through Java source? Unfortunately, the answer is yes. The Princeton team has
discovered at least one instance in which it is possible to create byte code for an activity that is not
allowed when going through a Java compiler. Other efforts to probe byte code functionality include
the University of Washington's Kimera Project and Mark LaDue's malicious applets. Byte code
banditry is as potent an approach today as it was in 1996.

Analyzing the Java VM


One problem affecting formal analysis of Java implementations is the size of the Java system. With
tens of thousands of lines of code, Java raises critical assurance flags. Making certain that each of
these lines of code does not introduce subtle vulnerabilities requires significant security analysis.
Only a bit of this sort of analysis has been performed.

It is beyond today's technical capability to formally verify any piece of code in excess of a few
thousand lines. This means that because of its size, Java is not amenable to formal proof of
correctness. However, it may well be worth the effort to formally prove some aspects of Java's
specification correct. The first targets should probably be the core of the VM and other security-
critical pieces of the JDK, such as Class Loaders and Security Managers.

Software Engineering

Many bugs have been found in various sections of the Java code. It is unlikely that security-critical
code is bug free. Security vulnerabilities are often the result of buggy software. It is difficult
enough to deal with bugs in standard code; bugs in security-critical code are much more serious.

This problem requires sound software engineering. That Java programs will be built out of
prefabricated components will make any security bugs much more serious. Many different sites
may end up using such a component that turns out to have a security problem. Not only will people
liberally borrow security-impaired code snippets from each other, they will also begin to reuse
entire classes of flawed code. Such code flaws will be increasingly difficult to isolate. Perhaps
software engineering will develop a new approach that avoids such potential pitfalls. In any case,
Java will continue to have an effect on what the future deems state of the art.

To Log or Not to Log

The next concern involves something very simple: keeping track of what Java does on your
machine. One universal capability that computer security experts rely on, no matter what the
platform involved, is logging. Often, the only way to reconstruct an intrusion is to carefully and
painstakingly read the associated log files. Of course, such detective work is not possible in an
environment lacking log files. Logs provide several benefits:

• They allow the victim to determine what damage was done.


• They provide clues about how to prevent similar attacks.
• They provide raw data for many intrusion detection approaches.
• They provide evidence for possible legal or administrative proceedings against the
perpetrator.

Java still has no logging capability (although as we shall see later in this chapter, a number of add-
on products provide this). It is impossible to track which applets were loaded and run, as well as
what those applets might have done. The most fundamental things that should be logged are file
system and network access. Simply capturing these data would give system and security managers
a chance to see what sorts of access were involved in an intrusion. File system access logging alone
would help system managers protect files that Java crackers were accessing in their break-in
attempts. It would also be good to capture applet byte code for analysis in case an applet ends up
doing something hostile. It is often easier to recover from an intrusion if you know what caused it
and what happened during the event.

Chapter 4 examines how an applet can delay its processing until a later time. Given that applets can
do this, logging becomes even more important. An especially crafty hostile applet can wait until
some other Web site becomes the main suspect before doing its dirty work. It won't be surprising if
the most hostile applets turn out to be the craftiest. Tracking byte code would give system
managers the ability to at least verify the function of each applet that may have been involved in an
attack.

One of the lessons emphasized in the book Takedown is that without a log file, it is impossible to
prosecute computer criminals [Shimomura and Markoff, 1996]. Without a log file, you have little
legal recourse in the event of a system break-in. If your site is hit by an attack applet today, erasing
critical information, you can't do anything about it, even if you know the culprit. Applet logging is
an essential security feature that should be made available immediately.

Who Do You Trust?

Early versions of Java were built without technological help for making privilege decisions. Since
1996, things have changed significantly; so much so that this book required a complete
revision. Chapter 3, "Beyond the Sandbox: Signed Code and Java 2," discusses the impact of the
new privilege system defined by Java 2 on the Java security situation.

Not only is the VM itself infused with the capability to create and enforce privilege policies, but the
very primitives out of which the new system is constructed have been made available to Java
developers. Java now includes support for standard cryptographic algorithms, including SHA,
MD5, DES (at least in North America), and SSL.

Now what is needed is tools to create and manage security policies that include privilege decisions.
Java 2 offers fine-grained access control, but it does not offer a compelling tool for creating,
testing, and managing policy (see Appendix C, "How to Sign Java Code"). Lack of such
management tools is likely to slow the adoption of Java 2 functionality in the enterprise.
Scattershot Security

One of the most common criticisms of early Java security architecture centered on how Java
spreads security functionality throughout the code. Unfortunately, the problem of scattershot
security has not gone away. Research at Princeton shows that security boundaries (between trusted
system code and less-trusted code) are crossed up to 30,000 times per second in a typical applet
[Wallach, et al., 1997]. Other evidence can be seen by the effort that Sun undertook when changing
the JDK 1.2 API from the beginPrivileged()/endPrivileged() syntax of beta3 to the doPrivileged()
syntax of beta4. Over 250 changes were required in the Sun reference VM implementation to make
the change.

Reliance on a scattershot architecture means that security depends on many different parts working
together properly. There is no centralized security system; no single source for security approval.
Java implements security features through dynamic type checking, byte code verification, class-
loading restrictions, and runtime checks performed by the Access Controller. Each resides in a
different part of the Java environment. Such an architecture depends on too many unrelated
functions. If all of the security-critical functions were collected together in one place, that aggregate
code could be more easily verified and analyzed. That simple step would satisfy some concerns
held by security experts.

Some of Java's security policies are dependent on the Java language itself. This is fine if all Java
byte code must be created by a Java compiler, but what guarantees does anyone have that byte code
has been generated by a Java compiler that plays by the rules? There are none, nor should there
ever be. There are compilers now in existence that turn Ada and C code into Java byte code. To
take such third-party byte-code development efforts away by legislating a particular compiler
would go against the spirit of the language.

The problem is that the Virtual Machine interpreting Java byte code may allow more functionality
than it should. More explicitly, there may be more functionality built in to the byte code than
security would dictate (see Figure 6.1). If the Java compiler never creates byte code capable of
exploiting such features of the VM, then the architecture would seem to remain safe. Since no one
has control over who and what creates Java byte code, system managers should not rely on such a
false hope. Someone could write a compiler able to create byte code that seems valid to the VM but
breaks rules ordinarily enforced by the Java compiler. Or, someone could create byte code through
any number of other means; for example, direct creation with an editor, or creation with a Java byte
code assembler (like Jasmin, see www.isbe.ch/ ~wwwinfo/sc/cb/tex/jasmin/guide.html).

Figure 6.1 If Java byte code is more powerful than


Java source code, then the extra functionality in byte
code is dangerous.

There is some evidence that this is the case.


One somewhat inefficient (but interesting) solution to this problem has been suggested by Andrew
Appel of Princeton. He suggests checking byte code by first decompiling it to Java source, then
recompiling the source to byte code. If a compiler you trust does not complain during
recompilation, then the original byte code is equivalent to some Java source program, and hence
must obey the rules of the Java language. This process is slow, but in certain security-critical
instances it pays to be paranoid.

Decompiling Java Byte Code

Although decompilation is not a traditional concern of security experts, it does have some
interesting twists in Java. It turns out that one of the side effects of Java byte code's clarity is that
byte code is very easy to decompile. This means that given a .class file, it is possible to
automatically reconstruct reasonable source code. (Of course, it is also possible to decompile x86
object code as well as any other executable code. Java is not alone in its exposure to
decompilation.)

The JDK comes with a weak decompiler as one of its standard tools, but much better decompilers
are available on the Web. In the early days, the best was the Mocha decompiler, which has since
become obsolete. A good decompiler to consider now is the SourceAgain Decompiler fromAhpah
software.

Decompilation is relevant to security for a couple of reasons. The first reason is that businesses
interested in using Java as a development language will need to consider the existence of
decompilers before they distribute Java .class files. It probably won't be possible to sell something
if making knock-offs turns out to be incredibly easy. Fortunately, some companies now distribute
Java source code obfuscators (watch out for snake oil solutions in this domain, however). The end
result of obfuscation is that although a .class file will decompile into valid Java, that valid Java
won't be very readable by humans. One caveat: Obfuscation certainly makes decompilation more
difficult, but it won't protect your code against a determined adversary.

Even if your code is subject to decompilation, you can still get some protection by copyrighting the
code and legally defending the copyright in court if necessary. This is not an ideal solution, but it's
better than nothing.

A closely related issue involves protecting secret or otherwise sensitive information in a piece of
mobile code, such as cryptographic keys. A good guideline if you are developing mobile code in
Java is not to include any secrets in the code. An applet that carries a password or a crypto key in
its code is amenable to hacking. Anyone who runs such code can get access to its secrets. More on
this issue can be found in Chapter 7, "Java Security Guidelines: Developing and Using Java More
Securely."

There is a third security concern related to decompilation. Given a piece of Java source code
obtained by decompilation, a cracker can better analyze the program for weaknesses that could be
exploited to break it. This would allow an attacker to attack a Java program more intelligently.
Applications like Netscape's Java VM are susceptible to this sort of source-related attack. Crackers
like to have code to poke around with. Furthermore, an attacker could build a very realistic Trojan
Horse program that looks almost exactly like the original. Like its ancient counterpart, a modern
Trojan Horse is a program that appears to be one thing at one level, but turns out to breach security
at another.

Trusted Dialogs and Meters

In an earlier chapter, we raised the idea of providing trusted dialog boxes for critical actions like
file I/O, or critical measurements such as CPU cycles used. These dialogs would provide an
important monitoring and feedback mechanism to Java users.

Providing a trusted set of dialogs (that cannot be spoofed) for things like file access seems like a
good idea. However, with any such user interface, one of the key goals must be to minimize user
involvement in security. Most users don't read their dialog boxes before they click OK (recall the
dancing pigs problem). Sophisticated users should probably have some control over their security
policies, but the less intrusive this control is, the better. Management issues like these are taking on
more importance as Java security evolves from the base sandbox into the Java 2 model in which
security policy plays such a central role. Centralized management is especially appealing at the
enterprise level, and much work remains to be done to develop policy management tools and
techniques.

Far from being in the way, a set of resource access indicators that cannot be forged would be a
welcome addition to Java from nearly every user's perspective. This set of instruments could allow
a user to track system resources such as CPU cycles, or microphone use. Some third-party vendors
offer monitoring capabilities like the ones mentioned here. What is not yet clear is how well
protected against spoofing these meters are. A meter that can be made to display false system
information on behalf of an attack applet is potentially more dangerous than having no meter at all.

Management Tools

Java 2 is not going to be adopted overnight; it is a complicated system, and utilizing it to its full
potential will be a complicated undertaking. As we have said before, we think it is likely that
signed mobile code and complex security policy will first be adopted for the intranet. Only after
organizations and enterprises have their ducks in a row internally will they begin to experiment
with complex security policies that make use of the Internet/Web.

A set of tools for creating and managing policy, especially enterprise-wide, would go a long way
toward easing the adoption of Java 2. The existing tools being distributed with the JDK are
rudimentary at best, and hard problems like identity/certificate management have many remaining
open issues. (See Appendix C for details on how to use some of the existing code signing tools.)

The problem of policy management has existed for years in the security community. One
characteristic of the problem is that it does not scale well. A tool that may be adequate to managing
policy for one browser will probably not work well across a network of hundreds or thousands of
machines. This problem crops up in all aspects of security. One common way to get a handle on it
is to create a choke point at the perimeter (for example, at the firewall) and instantiate site-wide
policy there. Security vendors have been frantically working on policy-management tools for some
time, but work remains to be done. Ideally, a site-wide policy could be managed by a powerful tool
and would include mobile code policy.

Many security pundits anticipated that by now, a solid public key infrastructure (PKI) would have
been put in place; unfortunately, that is not the case. Java 2 would be much easier to adopt if the
PKI were already there. As it now stands, delays in PKI placement are likely to hamper systems
that rely heavily on code-signing. After all, if you have no idea who is behind an identity, how can
you possibly trust them? It is not clear at this point why any particular certificate authority deserves
your trust.

Java Antidotes

As can be seen from the laundry list of high-level concerns, Java security can still be improved in
many ways. Some of the most effective antidotes to Java security problems involve addressing the
criticisms raised here.

CHAPTER SECTION:2

Section 2 -- Writing Safer Code: A Defensive Stance


It is all well and good to look to the future and determine what might make a platform stronger,
but developers and managers charged with using existing JDKs won't benefit immediately from
this strategy. Are there things developers can do now to make their Java code more secure?
Fortunately, the answer is yes. Chapter 7 introduces 12 technical guidelines for writing Java code
more securely. Also covered in Chapter 7 are user guidelines.

CHAPTER SECTION:3

Section 3 -- Third-Party Solutions or Snake Oil?


Sun Microsystems and the licensees of Java who have created particular Virtual Machines have
done an admirable job implementing a complex, language-based security model. Of course no one
is perfect. We discuss several serious problems in detail in Chapter 5. In light of the problems
detailed there (a majority of which have been addressed as Java has grown up), is there a need for
third-party Java security vendors?
The answer is complicated. On one hand, if Sun and the other major vendors (including the largest
software organizations on the planet) can't do it right, how can someone else? One answer is that
Sun is too busy working on the entire Java platform to pay enough attention to security concerns
and that little companies specializing in security can do better. Just for the record, we don't believe
that is true. Sun and the other Java vendors behave in a manner that shows they are truly concerned
with security issues. On the other hand, different organizations have different goals for their
security solutions. Some may demand features that are not included in off-the-shelf Java VMs. The
implication is that special-purpose solutions that address particular risks are needed. If that's true,
there should be plenty of room in the market for third-party security vendors to get involved.

As long as such third-party vendors concentrate on mitigating particular risks, their products will
add value. However, many such vendors are making overblown claims about what they can do to
enhance security. In this section, we want to clarify which risks can reasonably be addressed by
these vendors and which cannot.

In order to set the stage for a discussion of risks, we'll begin with a brief introduction to security
solutions that have been offered by third-party organizations.

Finjan

In the interest of full disclosure, we should note that both authors are members of Finjan's
Technical Advisory Board. Our membership on Finjan's TAB does not constitute an endorsement
of the company or its products.

Originally based in Israel and now headquartered in California, Finjan Software, Ltd. was among
the first third-party companies to address mobile code security issues (www.finjan.com). Finjan got
off to a rough start when some of its early marketing involved the use of scare tactics and what
might be called the "Chicken Little" approach to educating the public (and potential clients) about
security risks. Its approach to marketing has since matured significantly.

Finjan offers a couple of products created for both Win32 and Unix platforms. The first,
SurfinShield, is meant to be a browser-level application firewall. The idea is to add some
capabilities to the browser to help with mobile code management. SurfinShield comes in several
flavors. The second product, SurfinGate, is a firewall product that attempts to identify and
categorize mobile code as it arrives at the firewall. SurfinGate carries out some form of content
inspection that attempts to peer into the inner workings of Java applets (statically). SurfinCheck is a
less-powerful version of SurfinGate.

Finjan has had an interesting time in the mobile code security space and has been the target of some
particularly scathing criticism. Mark LaDue is especially outspoken about his concerns
(see www.rstcorp.com/hostile-applets/rube.html and www.rstcorp.com/hostile-
applets/drowning.html). Fortunately, Finjan has taken these criticisms to heart and seems to be
working diligently to provide better security products to its customers.
Digitivity (Citrix)

Digitivity, the commercial arm of APM, Ltd. in Cambridge, England, was recently acquired
by Citrix. The Digitivity approach to mobile code management is particularly sound from a
technical perspective. The idea is to identify and route all mobile code to a central server, where it
is then executed. This works because all GUI traffic is sent (by specialized protocol) to be
displayed on the client browser that originally requested the code. There are two main reasons to
centralize the execution of code like this: 1) to expose only the server, what Digitivity calls the
CAGE, to possible attack; and 2) to better manage the behavior of mobile code by knowing the
exact configuration of the mobile code platform. As we know, different Java VMs behave
differently even when they are running the same byte code. If you know for certain which VM a
piece of code will run on, it is easier to develop and manage custom enterprise solutions.

There is an inescapable irony to the Digitivity CAGE model. The very idea that spawned mobile
code systems like Java is the idea of taking advantage of distributed systems by running code on
client machines instead of running code on something like a centralized Web server. The CAGE
model centralizes Java code and works counter to the original Java concept. Still, there are
sometimes appealing reasons to centralize the execution of code, whether it is potentially dangerous
or not.

It is not clear how the CAGE approach will adapt to the new Java 2 security model. The main
strength of the CAGE approach is that the mobile code runs on a special machine that simply is not
allowed to access user files or initiate network connections under any circumstances. As soon as
partially privileged applets enter the picture, this simple approach goes out the window and the
CAGE machine faces many of the same problems as existing VMs.

Security7

Security7 is an Israeli company with offices in the United States. U.S. sales for Security7 are based
in Woburn, Massachusetts. The Security7 approach to mobile code security is to inspect HTTP
traffic coming in on all ports. Its SafeGate product requires Windows NT and is most effectively
implemented on a standalone box that acts as a proxy server for all HTTP traffic. The idea is to
enhance the existing firewall approach with the HTTP filtering system, which is built in to the OS
as a device driver. The SafeGate product includes the usual mobile code management capabilities
and logging.

It appears that Security7 has been active in spreading FUD (fear, uncertainty, and doubt) among
potential customers through an organization calledWithinReach. This strategy is reminiscent of the
early days of the antivirus industry in which some unscrupulous vendors were rumored to have
created and released actual viruses. These days, a code of ethics has been developed within the
antivirus community that does not permit the FUD strategy. Unscrupulous antivirus vendors are
quickly stamped out by more scrupulous vendors.

It is pretty obvious that Security7 and WithinReach are closely affiliated (in fact, Security7 has
openly admitted this). Assaf Arkin, a Security7 customer support manager, is also the
administrative and technical contact listed in the DNS records of WithinReach. Other evidence
includes the fact that one of the hostile applets hosted by WithinReach was signed with a digital
certificate registered to Richard Kosinski, Security7's vice president of marketing. The signature
has since been changed. InfoWorld broke a story about the relationship between Security7 and
WithinReach in late August 1998.

Until recently, the WithinReach site hosted a number of hostile applets, including a port of the Cult
of the Dead Cow's famous Back Orifice loader to Java. None of the applets on the site exploits any
security holes in Java; instead, the applets require permission to be granted by the user in order to
do anything harmful. That is, the applets are signed and request special permission to step outside
of the sandbox. The applets thus serve to emphasize the role that a human can play in mobile code
security. In this sense, the WithinReach applets may provide an interesting service. Nevertheless,
we do not believe that it is ethical for third-party security vendors to create security problems for
their products to address. Fortunately, Security7 seems to share our opinion now, and the
WithinReach Hostiles have been removed.

eSafe

eSafe is a Seattle-based company that also happens to be founded by an Israeli. eSafe offers two
products: Protect, a personal firewall, and Protect Gateway, a firewall-level filtering system. Protect
works by placing resource limitations on mobile code. The idea is to create a sandbox similar to the
Java sandbox within which all mobile code must run. The Protect Gateway product is a filter meant
to be added to an existing firewall that scans several protocols for mobile code. The filter includes
blacklisting capability. eSafe products also include antivirus capability in addition to addressing
mobile code security.

The Princeton Secure Internet Programming Team's JavaFilter

The Princeton team distributes a black-listing/white-listing Class Loader for JDK 1.0.2 that can
automatically allow or disallow applets to be loaded from particular sites. The filter replaces Java's
normal AppletClassLoader with a new implementation that checks URLs and policy before
fetching code. Although this is not a commercial product, it is certainly a third-party add-on that
some organizations may find useful. Seewww.cs.princeton.edu/sip/JavaFilter for more information.

ICSA's Mobile Code Security Consortium

Regardless of its misleading name, the International Computer Security Association is a for-profit
business that licenses the use of trademarked "certification" logos. (Perhaps its name is a perfect
example of spoofing?!) ICSA was known as NCSA until 1998 when it changed its name from
"National" to "International." As part of its business, ICSA creates consortia of security vendors
interested in particular issues. The consortia exist to hash out self-imposed certification criteria that
can be used as a marketing edge by vendors. ICSA's business strategy is to get vendors to pay to be
in a consortium (in which vendors create certification criteria) and then pay to be certified on a
subscription basis. Consumer purchasing decisions are often swayed, for whatever reason, by ICSA
certification marks.

One of ICSA's best-known certification products involves certifying firewalls. The firewall
certification system makes an interesting case study. As we understand it, ICSA's firewall
certification process boils down to running a penetration test against one instance of a firewall
product as configured according to printed instructions from the firewall vendor. If a firewall
product passes the tests, the vendor is allowed to place an ICSA certification stamp on its box (and
use it in its marketing literature).

A potentially fatal flaw in this firewall certification scheme is that firewalls are not really black-box
systems; instead, they are programmable systems that are only as strong as the rules within them
defining security policy. Rules in a typical firewall say things like "only allow machines having an
IP address within the proper range to send HTTP traffic through port 80." The flaw in the ICSA
certification scheme becomes obvious considering that what is being certified is a particular
configuration of a firewall. The problem is that firewalls are particularly easy to misconfigure, and
the certification scheme says nothing about how difficult it is for an end user of a firewall to
configure it correctly. Firewall purchasers may be misled into thinking that buying an ICSA-
certified firewall guarantees some minimum level of security. That is not the case. For a stark view
of the ICSA firewall certification process, see firewall guru Marcus Ranum's page
at www.clark.net/pub/mjr/pubs/fwtest/index.htm. On the flip side of the coin, the firewall
certification process that ICSA offers is said to be a discriminator; that is, some firewall products
are unable to pass the security tests even when configured according to the instructions! So the
certification has at least that benefit to offer.

We raise the firewall certification issue because ICSA recently announced the formation of a
consortium meant to address mobile code security. Current members of the consortium include
Digitivity, eSafe, Finjan, Internet Security Systems, Security7, and Symantec (among others).
Strikingly, the major vendors of security-critical platforms for mobile code (Sun, Netscape, and
Microsoft) are missing from the list. The question raised earlier in this chapter about what third-
party vendors can offer (as opposed to the platform architects) comes to mind.

It is likely that the mobile code consortium will choose to invent a set of self-imposed standards (a
certification process) for itself. The question to ask is, what will this certification amount to?
Developing a certification standard for the mobile code consortium is no easy task if it is to be done
properly. Without external review by experts, a mobile code security certification may not actually
mean anything. It is our hope that ICSA will take the opportunity to do things right.

CHAPTER SECTION:4
Section 4 -- Risks That Third-Party Vendors Can
Address
As we mentioned in the previous section, there are a number of Java security needs that third-party
vendors can help to address. We discuss such needs here and also raise some hard questions that
you should ask vendors of such products if you're considering buying them. Perhaps
counterintuitively, some concerns included in this section are also covered in the next, which
discusses risks that can't be easily addressed. That's because the issues are complicated and can be
seen equally well from both points of view.

Logging

For a discussion of why logging is important, see page 194. The Java platform in most browsers
does not do an adequate job of logging mobile code. This is an area in which third-party software
can add real value. However, logging is actually trickier than it might appear at first blush.

What to Ask about Logging


The main thing to ask of any logging capability is, how well protected is the log file from being
overwritten, corrupted, or deleted? Attackers often cover their tracks by editing log files. Attack
applets of the sort we discussed in Chapter 5 could certainly change logs if they wanted to.
Accuracy is also a big issue for log files. It is important that logs collect proper data and can't be
easily mislead by a malicious process.

If vendors say that their products log all incoming byte code, you should ask how they can be sure.
This is roughly the same issue we describe next when we talk about applet isolation. After all, if
code can slip past your firewall, how can you be sure it can't slip past the logging facility?

Applet Isolation (and Identification)

A common approach to security for many companies includes the installation of sacrificial lamb
machines in a DMZ (terminology borrowed from military speak). Things like Web servers and FTP
servers are well-situated in DMZs because they are common targets of attack with many well-
known and commonly exploited security holes. The idea is to create a subnetwork behind the
firewall but isolated from the rest of your network. That way, if the servers are hacked, your site is
still not completely compromised.

Isolating untrusted code to a server in the DMZ may be a good idea, depending on your mobile
code risk analysis. If mobile code turns out to be hostile and aims an attack at the server it is
running on, much less is lost if the server is a sacrificial lamb in the DMZ. Some third-party
solutions offer this capability.
What to Ask about Applet Isolation
One interesting question to ask vendors of isolation products is, how well do they identify applets
in the first place? It turns out that trapping all applets is not an easy problem. Early approaches
relying on scanning port 80 traffic for the <APPLET> tag have been shown to be inadequate
(something we mentioned in 1996). One of the most thorough treatments of this issue can be found
in a paper called Blocking Java Applets at the Firewall [Martin, et al., 1997]. The paper points out
problems with standard blocking methods and discusses the technical difficulties of blocking.

Another hard question for vendors involves the use of encrypted channels for mobile code delivery.
Most browsers have built-in capability to create an SSL connection between the Web server and the
client browser (see Chapter 3). Once an SSL connection exists, all traffic between the browser and
the server is encrypted. That means a firewall will not be able to parse the traffic as it goes by.
What a firewall can't see, it certainly can't identify. Ask vendors what they do about encrypted
communication in their identification approach. If they can't scan SSL traffic, they're in trouble
because an attacker can defeat their scanner by changing http to https in the attack page's URL.

Black Listing

The idea of creating a list of applets known to be hostile seems like a really good idea. The key is to
identify hostile applets by something less easy to change than their name or the URL from which
they came. Some systems use hashing algorithms like MD5 to create a database of applets on a
black list. Others offer the ability to black list entire Web servers.

Assuming applets can be identified in the first place, black listing will work. One problem is that it
is almost trivial to make a hostile applet that changes its own byte code (in a very simple way) each
time it is requested from a server. Mark LaDue's HAMGen applet does this.
Seehttp://www.rstcorp.com/hostile-applets/Rube/HAMGen.java. HAMGen stands for Hostile
Applet Mutation Generator.

What to Ask about Black Listing


It should be clear that black listing by itself does not provide adequate security; in fact, all by itself,
black listing is darn close to useless. It's too easy to make trivial changes in a malicious applet (a
process that can be automated at the Web server end) to thwart almost every black-listing scheme.

Nevertheless, black listing is so cheap to implement that it may make sense to do it anyway,
especially if you enlist the help of a Class Loader. Using a system that blacklists known hostile
code may be worthwhile even if it only stops the occasional naïve attack.

Trusted Dialogs and Meters


We discussed trusted dialogs and meters earlier in this chapter on page 198. Such dialogs are
particularly useful to managers whose policy involves having users keep an eye out for unusual
activity. They are also useful to more savvy users who know about mobile code. The problem with
most VMs today is that they give no indication that anything special is happening when mobile
code is running, be it Java, JavaScript, or ActiveX. The behind-the-scenes approach gives the user a
more seamless Web experience, but it makes the job of writing a hostile piece of mobile code that
much easier.

There is a downside to providing dialogs and meters-the user can become overwhelmed with
security-related dialogs leading to the "cry wolf" phenomenon. It's easy to click OK over and over,
especially when you're annoyed. Trusted meters and dialogs would best be as nonintrusive as
possible.

What to Ask about Trusted Dialogs and Meters


A particularly hard question to ask is, how can dialogs and meters avoid the spoofing problem? In
other words, how can you be sure that the "meter" you see on the screen is real and isn't really
being drawn by a hostile applet? If these things can't be trusted to tell you the truth, then they're not
worth the pixels they're made of. Turns out this problem is a tough one. A nontrustworthy meter is
potentially worse than no meter at all.

Platform Proliferation

One of the most successful Java marketing phrases is "write once, run anywhere." A number of
parodies of this phrase have been floated; among the best is "write once, test everywhere." The
problem is that all Java VMs don't always do the same thing given the same byte code. Arbitrary
behavior of similar code in different versions of a language is a problem that Java was supposed to
solve.

This problem can introduce real security risks. Consider, for example, that the attacks described in
Chapter 5 still work against old versions of browsers out there. Unless you have an up-to-date
browser, you may be surfing dangerously. Now imagine that you are the administrator for a large
site with hundreds of users. How likely is it that all your users are using up-to-date browsers? How
likely is it that they're even using browsers from a single vendor? Some sites address this problem
by mandating the use of a particular (often out-dated) browser; however, impatient power users will
download their own free browser and install it themselves.

Using a move that mirrors the Applet Isolation idea discussed previously, it is possible to address
this risk by forcing all applets to run on a central server. That way, you can guarantee things about
the environment in which mobile code will run.

What to Ask about Centralized Server Solutions


A big issue for all centralized systems is scalability. How many users can bog down a central server
before it chokes? How big a server do you need? What protocol is used to direct GUI traffic
between the server and the clients running on normal browsers? How well protected is that protocol
against simple denial-of-service attacks? And given the difficulties in detecting incoming applets,
how can you be sure that all applets are being redirected to the central server?

Policy Management
Finally, we come around to policy management again. Given the state of existing tools, there
should be some serious demand for good policy management systems.

Note that the management issue is not unique to security. Managing large networks of many
machines is a daunting task, and the tools are not yet good enough. Consider the problem of
keeping all the software on hundreds of PCs up to date. To some extent this is a problem introduced
by the complications of distributed systems. Security in merely one aspect of this broader
management problem.

What to Ask about Policy Management


Policy management is as much a business management problem as it is a technological problem.
Human factors enter the picture, such as, who is allowed to set policy and who can edit it? An
excellent goal to strive for is to put in place a site-wide security policy that speaks to all security
issues. Much work remains to be done.

CHAPTER SECTION:5

Section 5 -- Risks That Third-Party Vendors Can't


Address
It seems that there is indeed room for third-party vendors to get involved in the mobile code
security business. However, many mobile code security issues cannot be addressed in a trivial
manner. In this section, we raise a number of tricky issues that are harder to deal with than they
seem at first. Many of these issues are reflected in the questions to ask from the previous section.
Keep these points in mind when you are shopping for mobile code solutions.

Identifying Applets Entering the System

As we pointed out in 1996, identifying applets is much harder than simply scanning for the
<APPLET> tag in port 80 HTTP traffic. The problem of encrypted communication using
something like SSL is especially hard to deal with. (Similar problems crop up with encrypted mail
that includes mobile code.) The only completely guaranteed way of identifying all mobile code
before it runs is by stopping it as it enters the VM. Your browser certainly knows when it
encounters a Java applet, and it is probably in the best position to do something about it.
From a management perspective, identifying mobile code at the firewall has more appeal than
trying to identify it on every desktop in an organization. This is especially true of organizations in
which employees do not follow security policy. However, the problem is nontrivial. Ask hard
questions about systems that claim to be able to stop all mobile code. Make sure the answers deal
with all ports, multiple protocols, and encrypted connections.

Killing Applets

Some vendors will tell you that their products allow the user to kill a running applet if it is
misbehaving. Although many people are surprised to hear it, there's really no effective way to kill
an applet if the applet doesn't want to die. The problem is that the only way to kill a Java thread is
to throw an Exception at it, and the thread can simply catch the Exception. See page 125.

You might think that this problem can be solved by creating a new kind of Exception that cannot be
caught, but that won't solve the problem either. The difficulty is that the try/finally statement in the
Java language allows a program to do an arbitrary amount of "cleanup" work before it finishes
running. In fact, the "cleanup" code can simply carry on the work of the thread you wanted to kill.

An even stronger way to try killing an applet is simply to stop all of the applet's threads, not even
letting them execute their cleanup code. This doesn't work either, in general, because the stopped
threads may be holding locks, and the VM may be unable to make progress without the locks.
Revoking the locks while the victim threads are in the middle of using them only makes things
worse, leaving the VM in a potentially inconsistent state.

This gets pretty complicated, but the bottom line is that there's no way to safely kill a thread that
doesn't want to die. And if an applet thread is trying to cause trouble, why would it willingly let you
kill it?

Of course, you can stop all running applets by killing the VM or browser entirely, but that's a pretty
drastic solution, and you don't need some fancy tool to let you do it.

Scanning Applets

Java has important dynamic properties. Because of Java's dynamic loading capabilities, it is not
possible to statically analyze a Java class completely. Some analysis will always have to be put off
until runtime. The fact that the Verifier has a runtime phase is no accident (see Chapter 2).

Java's dynamic capabilities do not bode well for systems that claim to be able to scan applets for
potentially harmful behavior. After all, if the code is not all there (or if it includes dangling
references to not-yet-loaded code), how can you scan it? Static analysis is not sufficient to ensure
security, and short of firing up a VM, that's all you can do.
Another problem is that scanning works according to the assumption that attack code can be
categorized as such. We have seen (or written) plenty of attack code in the lab. It is not the case that
this code can be identified as attack code before it does its dirty work. For example, one of the
attacks described in Chapter 5 can be written as an applet that carries out seemingly innocuous
type-casting operations, one after another. The casting actually implements the important part of a
type-confusion attack. Looking at the code, it would be impossible to tell that the applet is an attack
applet.

Many of the malicious applets written as demonstrations use very obvious techniques to carry out
their hostile activities. For example, early denial-of-service or monitoring applets redefined the
stop() method. This does not mean that every applet that redefines stop is malicious, nor does it
mean that each and every attack applet will do so.

Scanning for unknown malicious behavior is a very difficult task that is certainly impossible in
theory.

Trusted Dialogs and Meters

Providing security-related feedback to a user is a good thing, as long as the information is correct.
However, if a meter can be spoofed, you can bet that a hostile applet will do so. The spoofing
problem may be severe enough that it is best not to have meters. This may in fact be why Sun,
Netscape, and Microsoft do not include such feedback mechanisms in their implementations.

Ask yourself why you should trust a meter and how you can ensure that it is giving you the correct
information. Improving the VM's Security Model In the end, the most bang for the buck in terms of
security will come from Sun and the Java licensees who create the VMs and APIs. Compared to
creating a complete VM with added security mechanisms, improving the security situation by
adding on minor services is probably nowhere near as effective.

CHAPTER SECTION:6

Section 6 -- Assess Your Risks


In some sense, the entire purpose of this book is summed up by the phrase "assess your risks." Our
goal in writing this book is to make you aware of what is going on with Java security. That way,
you can make an intelligent, informed decision about what to do as a user, as a developer, as a
manager, or as a business decision-maker.

Each organization and individual must create their own strategy for developing, using, and
managing Java. The way to do this is to take a long hard look at the risks incurred through Java use.
If these risks turn out to be too much to bear, then you should probably reconsider being connected
to the Internet itself! Using Java is risky, but really not much more risky than simply being on the
Net.

Risk assessment involves understanding what it would mean if the data on your machine were
made public, what it would mean if your machine were to stop functioning, and what it would
mean if the performance of your machine were suddenly and seriously degraded. Risks differ
according to context. That means if you have more than one machine (and most organizations do),
it is likely that risk assessments for each one differ.

An intelligent Java strategy can only be made after understanding what you have to lose. If the
answer is "nothing," then there is no reason to worry about Java. If the answer is "the business,"
then perhaps a more comprehensive Java security policy should be put in place.

When you are considering your risks, make sure you don't discount the benefits of Java. Java is an
exciting and interesting technology that has lots to offer. Try not to throw out the good with the
bad.

Java Security Guidelines


Developing and Using Java More
Securely
Java security is important to Web users, system administrators, Java developers, and business
people. Sun Microsystems and others have worked hard to evolve a Java system with which to
create and use more secure code. Java is not immune to security risks, however. As we have seen,
designing and implementing a language-based security model is not easy, and mistakes are bound
to happen.

Given that there is no such thing as a 100-percent secure system (at least if we want that system to
do anything useful), how can developers create more secure Java code? And, given that Java
developers can't always be counted among the good guys, how can Web users tailor their Web use
to be more secure? These are the two major questions this chapter answers. We introduce two sets
of guidelines in this chapter: developer guidelines for creating more secure Java code and user
guidelines for avoiding risks when using Java. By establishing some simple security guidelines,
you can avoid most of the risks discussed throughout this book. Many of the user guidelines have
been touched on in other chapters. Here they are consolidated in a complete package. On the other
hand, the developer guidelines are completely new material.
Chapter Seven Sections
1. Guidelines for Java Developers
2. Guidelines for Java Users
3. Guidelines Are Never Perfect

Java Security Guidelines: Developing and Using Java


More Securely
CHAPTER SECTION:1

Section 1 -- Guidelines for Java Developers


This section introduces 12 rules for writing security-critical Java code; 12 rules that all Java
developers should abide by. If you are charged with managing a gaggle of Java developers, or if
your business relies on the security of Java, make sure your developers follow these rules.

These rules have not been sugar-coated for mass consumption. They get fairly technical and require
broad knowledge of Java. Although experienced Java developers will understand all of the rules,
less-experienced Java developers may have a bit of homework to do. Nevertheless, these rules are
important and can make your Java code more secure.

The rules listed here were built on the experiences of many people who have generously discussed
their experiences in building secure Java code. We are particularly grateful to Andrew Appel, Dirk
Balfanz, Drew Dean, and Dan Wallach, of the Secure Internet Programming Team at Princeton, for
helping us understand these issues. Others who have contributed significantly to the behind-the-
scenes thinking that went into these rules include David Hopwood, Li Gong, and Jim Roskind.

The rules are based on much experience in hunting down Java security bugs, and on advice and
observations from people who write and review security-critical Java code for a living. Each rule is
designed to eliminate an unexpected "gotcha" that you might face.

Of course, security is an elusive goal, and following these rules certainly won't provide any
guarantee that your code is secure. It is easy to write insecure code that follows these rules. The
goal of these rules is not to guarantee security, but to eliminate certain kinds of security attacks that
you might not have thought of. If you follow these rules, certain kinds of attacks will be
impossible; other kinds will still be possible. So think of these rules as a first step. If you are
writing code that may be linked or run in conjunction with untrusted code, then you should
definitely consider following these rules.

Every attempt was made to keep the rules simple enough that you can treat them as a checklist to
be followed in mechanical fashion. That way, you can save your brainpower for other security
issues.
Rule 1: Don't Depend on Initialization

Most Java developers think that there is no way to allocate an object without running a constructor.
This is not true: There are several ways to allocate uninitialized objects.

The easy way to protect yourself against this problem is to write your classes so that before any
object does anything, it verifies that it has been initialized. You can do this as follows:

• Make all variables private. If you want to allow outside code to access variables in an object,
this should be done via get/set methods. (This keeps outside code from accessing
uninitialized variables.) If you're following Rule 3, you'll make the get and set methods final.
• Add a new private boolean variable, called initialized, to each object.
• Have each constructor set the initialized variable as its last action before returning.
• Have each nonconstructor method verify that initialized is true, before doing anything. (Note
that you may have to make exceptions to this rule for methods that are called by your
constructors. If you do this, it is best to make the constructors call only private methods.)
• If your class has a static initializer, you will need to do the same thing at the class level.
Specifically, for any class that has a static initializer, follow these steps:
• Make all static variables private. If you want to allow outside code to access static variables
in the class, this should be done via static get/set methods. This keeps outside code from
accessing uninitialized static variables. If you're following Rule 3, you'll make the get and
set methods final.
• Add a new private static boolean variable, called classInitialized to the class.
• Have the static constructor set the classInitialized variable as its last action before returning.
• Have each static method, and each constructor, verify that classInitialized is true, before
doing anything. (Note: Constructors are required to call a constructor of the superclass or
another constructor of the same class as their first action. Therefore, you will have to do that
before you check classInitialized.)

Rule 2: Limit Access to Your Classes, Methods, and Variables

Every class, method, and variable that is not private provides a potential entry point for an attacker.
By default, everything should be private. Make something non-private only if there is a good
reason, and document that reason.

Rule 3: Make Everything Final, Unless There's a Good Reason Not To

If a class or method is non-final, an attacker could try to extend it in a dangerous and unforeseen
way. By default, everything should be final. Make something non-final only if there is a good
reason, and document that reason.
You might think that you can prevent an attacker from extending your class or its methods by
declaring the class non-public. However, if a class is not public, it must be accessible from within
the same package, and as we shall see, Rule 4 says not to rely on package-scope access restrictions
for security.

This advice may seem harsh. After all, the rule is asking you to give up extensibility, which is one
of the main benefits of using an object-oriented language like Java. When you're trying to provide
security, however, extensibility is your enemy; it just provides an attacker with more ways to cause
trouble.

Rule 4: Don't Depend on Package Scope

Classes, methods, and variables that are not explicitly labeled as public, private, or protected are
accessible within the same package. Don't rely on this for security. Java classes are not closed, so
an attacker could introduce a new class inside your package, and use this new class to access the
things you thought you were hiding. (A few packages, such as java.lang, are closed by default, and
a few JVMs let you close your own packages. However, you're better off assuming that packages
are not closed.)

Package scope makes a lot of sense from a software-engineering standpoint, since it prevents
innocent, accidental access to things that you want to hide. But don't depend on it for security.
Maybe we'll get sealed classes in the future.

Rule 5: Don't Use Inner Classes

Some Java language books say that inner classes can only be accessed by the outer classes that
enclose them. This is not true. Java byte code has no concept of inner classes, so inner classes are
translated by the compiler into ordinary classes that happen to be accessible to any code in the same
package. And Rule 4 says not to depend on package scope for protection.

But wait, it gets worse. An inner class gets access to the fields of the enclosing outer class, even if
these fields are declared private. And the inner class is translated into a separate class. In order to
allow this separate class access to the fields of the outer class, the compiler silently changes these
fields from private to package scope! It's bad enough that the inner class is exposed, but it's even
worse that the compiler is silently overruling your decision to make some fields private. Don't use
inner classes if you can help it. (Ironically, the new Java 2 doPrivileged() API usage guidelines
suggest that you use an inner class to write privileged code. That's one reason we don't like the
doPrivileged() API.)

Rule 6: Avoid Signing Your Code


Code that is not signed will run without any special privileges. And if your code has no special
privileges, then it is much less likely to do damage.

Of course, some of your code might have to acquire and use privileges to perform some dangerous
operation. Work hard to minimize the amount of privileged code, and audit the privileged code
more carefully than the rest.

Rule 7: If You Must Sign Your Code, Put It All in One Archive File

The goal of this rule is to prevent an attacker from carrying out a mix-and-match attack in which
the attacker constructs a new applet or library that links some of your signed classes together with
malicious classes, or links together signed classes that you never meant to be used together. By
signing a group of classes together, you make this attack more difficult.

Existing code-signing systems do an inadequate job of preventing mix-and-match attacks, so this


rule cannot prevent such attacks completely. But using a single archive can't hurt.

Some code-signing systems let you examine other classes to see who signed them. If you are using
a code-signing system that allows this, you can put code into the static constructors of your classes
to verify that the "surrounding" classes have been signed by the same person as expected.
Examining signers is one way to avoid the example shown in Figure 7.1. This doesn't completely
prevent mix-and-match attacks, since an adversary can still mix together classes that you signed at
different times; for example, by mixing version 1 of Class A with version 2 of Class B. If you're
worried about this kind of interversion mix-and-match attack, you can put each class's "version
stamp" in a public final variable and then have each class check the version stamps of its
surrounding classes.

Figure 7.1 A mix and match attack.

In one type of mix and match attack, signed code


with special privilege is linked or otherwise
grouped together with unsigned code. The
danger is that the unsigned code will be replaced
in the group, leading to undefined and possibly
dangerous behavior.

Rule 8: Make Your Classes Uncloneable

Java's object-cloning mechanism can allow an attacker to manufacture new instances of classes you
define, without executing any of your constructors. If your class is not cloneable, the attacker can
define a subclass of your class, and make the subclass implement java.lang.Cloneable. This allows
the attacker to make new instances of your class. The new instances are made by copying the
memory images of existing objects; although this is sometimes an acceptable way to make a new
object, it often is not.

Rather than worry about this, you're better off making your objects uncloneable. You can do this by
defining the following method in each of your classes:

public final void clone() throws java.lang.CloneNotSupportedException {


throw new java.lang.CloneNotSupportedException();
}

If you want your class to be cloneable, and you've considered the consequences of that choice, then
you can still protect yourself. If you're defining a clone method yourself, make it final. If you're
relying on a nonfinal clone method in one of your superclasses, then define this method:

public final void clone() throws java.lang.CloneNotSupportedException {


super.clone();
}

This prevents an attacker from redefining your clone method.

Rule 9: Make Your Classes Unserializeable

Serialization is dangerous because it allows adversaries to get their hands on the internal state of
your objects. An adversary can serialize one of your objects into a byte array that can be read. This
allows the adversary to inspect the full internal state of your object, including any fields you
marked private as well as the internal state of any objects you reference.

To prevent this, you can make your object impossible to serialize. The way to do this is to declare
the writeObject method:

private final void writeObject(ObjectOutputStream out)


throws java.io.IOException {
throw new java.io.IOException("Object cannot be serialized");
}

This method is declared final so that a subclass defined by the adversary cannot override it.

Rule 10: Make Your Classes Undeserializeable

This rule is even more important than the preceding one. Even if your class is not serializeable, it
may still be deserializeable. An adversary can create a sequence of bytes that happens to deserialize
to an instance of your class. This is dangerous, since you do not have control over what state the
deserialized object is in. You can think of deserialization as another kind of public constructor for
your object; unfortunately, it is a kind of constructor that is difficult for you to control.

You can prevent this kind of attack by making it impossible to deserialize a byte stream into an
instance of your class. You can do this by declaring the readObject method:

private final void readObject(ObjectInputStream in)


throws java.io.IOException {
throw new java.io.IOException("Class cannot be deserialized");
}

As in Rule 9, this method is declared final to prevent the adversary from overriding it.

Rule 11: Don't Compare Classes by Name

Sometimes you want to compare the classes of two objects to see whether they are the same, or you
want to see whether an object has a particular class. When you do this, you need to be aware that
there can be multiple classes with the same name in a JVM. It is a mistake to compare classes by
name since different classes can have the same name.

A better way is to compare class objects for equality directly. For example, given two objects, a and
b, if you want to see whether they are the same class, you should use this code:

if(a.getClass() == b.getClass()){
// objects have the same class
}else{
// objects have different classes
}

You should also be on the lookout for cases of less-direct by-name comparisons. Suppose, for
example, you want to see whether an object "has the class Foo." Here is the wrong way to do it:

if(obj.getClass().getName().equals("Foo")) // Wrong!
// objects class is named Foo
}else{
// object's class has some other name
}

Here is a better way to do it:

if(obj.getClass() == this.getClassLoader().loadClass("Foo")){
// object's class is equal to the class that this class calls "Foo"
}else{
// object's class is not equal to the class that
// this class calls "Foo"
}

Note the legalistic comments in the last example. Whenever you use classnames, you are opening
yourself up to mix-and-match attacks, as described in Rule 7. You should also know that the Java
language forces you to use classnames all the time: in variable declarations, instanceof expressions,
and exception-catching blocks. Only the designers of Java can prevent mix-and-match attacks, but
you can avoid making the problem worse by avoiding by-name class comparisons.

Rule 12: Secrets Stored in Your Code Won't Protect You

You might be tempted to store secrets such as cryptographic keys in the code for your application
or library. Secrets stored in this way are completely accessible to anyone who runs your code.
There is nothing to stop a malicious programmer or virtual machine from looking inside your code
and learning its secrets.

Code obfuscation is another way to store a secret in your code; in the case of obfuscation, the secret
is simply the algorithm used by your code. There's not much harm in using an obfuscator, but you
shouldn't believe that it provides strong protection. There is no real evidence that it is possible to
obfuscate Java source code or byte code so that a dedicated adversary with good tools cannot
reverse the obfuscation.

The Take Home Message

Writing secure Java code is very difficult. There is no magic bullet that will solve your security
problems; all you can do is think hard (perhaps with help from formal analysis tools) and use
prudent engineering practices to minimize risks. Sometimes a pair of objective outside eyes can
help. The rules set forth here are intended to describe some prudent engineering practices for
writing secure Java code. They won't solve your security problems, but they will reduce the number
of ways in which things can go wrong.

CHAPTER SECTION:2

Section 2 -- Guidelines for Java Users


It is all well and good to talk about what can be done to improve the next version of Java, or how
you as a developer can create safer code, but what can a regular Java user do to stay safe? Are there
guidelines for safely using Java as it exists today?

There are several straightforward things you can do to make your use of Java safer. Most of these
are based on good old-fashioned common sense; others require a bit of knowledge about Java. We
have compiled a set of guidelines from the other chapters and have organized them here.

• Know what Web sites you are visiting.


• Learn as much as you can about Java security.
• Know your Java environment.
• Use up-to-date browsers with the latest security updates.
• Keep a lookout for security alerts.
• Apply drastic measures if your information is truly critical.
• Assess your risks.

Know What Web Sites You Are Visiting

The first piece of advice we have is of the common-sense variety: Know what sorts of sites you are
visiting with your Web browser. The chances of being attacked by an applet from a large corporate
site like www.sun.com are minimal. The chances of suffering an attack while surfing an
underground cracker Web page are, of course, much greater.

All dangerous Web sites may not be run by bad guys, however. One interesting cracker strategy
might be to hack a Web server in a very public fashion (something that has been done to the U.S.
Department of Justice, the U.S. Air Force, and the Central Intelligence Agency, among others
[Ghosh, 1998]), but add a twist. The media coverage will cause many thousands of people to surf to
the hacked site to "check it out." Here's where an attack applet would do the most damage! Since
the Web server has been hacked, the cracker figures, why not install some mobile code that attacks
all surfers indiscriminately? All the curiosity seekers thus become de facto targets.

In any case, there are certainly unsafe places on the Web. An analogy may be useful here. Both of
the authors are sorry to admit they drive minivans. There are places in a large city such as
Washington, D.C. where no one in his or her right mind would drive a minivan. Just as you
shouldn't drive your minivan in certain areas of the city, you shouldn't drive your Java-enabled
browser to certain areas of the Web.

You can avoid risk-laden Web sites by employing the same strategy you would use when visiting a
big city. Use your intuition to assess your environment continually when you surf. If you are
tempted to visit some cracker Web sites, do so with Java turned off. (Actually, make sure that all
types of executable content are disabled when visiting such sites!)

The Internet and the Web are reflections of society, no more and no less. There are sites that reflect
the pinnacle of what people can achieve, and there are those that reflect the depths to which people
can lower themselves. Know that just as there are bad people who rob banks and mug people in the
real world, there are bad people on the Web. Some of these people create traps to prey on
unsuspecting surfers.

Depending on your level of paranoia, you might consider all unknown Web sites as risky locations.
A good strategy might be to leave Java off by default and turn it on only when you make a
conscious decision to trust a site. Many business sites require that their employees use Java this
way. This strategy is easy to implement. We discussed how to disable and enable Java on two
popular browsers in Chapter 4, "Malicious Applets: Avoiding a Common Nuisance."
This guideline takes on more weight as the world rushes into electronic commerce. If your PC has
data on it that help you transact business over the Net (such as trading stocks) then you need to be
concerned about computer security. The addition of money (virtual or not) to the Net changes the
stakes significantly.

Learn as Much as You Can about Java Security

By reading this book, you are off to a good start. Keep in mind, however, that Java security is a
fast-moving field. Since Java's introduction in 1996, the security landscape has changed
dramatically:

• Tens of millions more people are connected to the Internet.


• Java has undergone two major releases.
• The security model has evolved to include signed applets.

There is no reason to believe that Java's evolution will slow. In fact, Java's quick move to maturity
is essential to its long-term viability as a language.

One way to keep abreast of Java security happenings is to sign up for notification of breaking Java
security news. On this book's companion Web site at www.rstcorp.com/java-security.html, you can
submit a form to be added to our notification mailing list. Note that this list will not be sold, rented,
or otherwise shared with anyone other than the authors. We promise. We hate spam as much as
anyone!

You should also keep tabs on the Java Security Hotlist (see Appendix B, "The Java Security
Hotlist," and www.rstcorp.com/javasecurity/links.html).

Know Your Java Environment

Another piece of common-sense advice is to know about the Java tools that you are using. Know
how Java works (this book should help). Now that you know how Java treats signed classes with
privilege, you know how important it is to consider carefully what your security policy looks like
and who created it. Also be aware that early versions of Java (including JDK 1.1) allow built-in
code full power to do anything.

Before Java 2, built-in classes were allowed to bypass all security verification checks. If you are
using a browser that does not yet support Java 2, never put Java classes (or .jar archives of classes)
in your CLASSPATH unless you fully trust the vendor that distributed them. Also be aware that
any subdirectories under the directories in your CLASSPATH may be searched for a class as well.
Know who built your Java libraries and tools.

Similarly, do not install or otherwise change your Java security policy under the influence of a
vendor or other mobile code distributor. A potential social engineering attack against a site running
Java 2 could involve convincing a user to redefine security policy in order to run some cool Java
code. If you must change policy, think long and hard about why and about who you may be
trusting.

Be aware of what Java version you are using. Several companies have licensed the right to produce
Java development environments. Some probably do a better job with security than others. If the
Java VM is buggy, then Java security is compromised. By using a particular vendor's version of
Java, you are trusting the security of your machine to them.

This is actually a general lesson that applies to many technologies. Browser plugins should be
subject to similar scrutiny. So, too, should any executable binaries loaded from the Internet.

Finally, find out who controls your browser's Java security policy. Some corporate users may be
surprised to learn that they are not in control of their surfing security policy. Starting with version
4, both Netscape Navigator and Microsoft Internet Explorer allow a system administrator to set
security policy and lock it in so that it cannot be overridden by users. Some sites attempt to block
Java and other mobile code traffic at the firewall (although see Chapter 6, "Securing Java:
Improvements, Solutions, and Snake Oil"). If you do control your own policy, make sure you set up
a policy that is sound and coherent. Be aware that setting up a security policy is nontrivial and often
leads to subtle errors.

Use Up-to-Date Browsers with the Latest Security Updates

This guideline may be a bit counterintuitive, but you should always use the latest version of your
favorite browser-even if it is a beta version. All of the security holes discussed in Chapter 5,
"Attack Applets: Exploiting Holes in the Security Model," have been fixed in the very latest
versions of Netscape and Internet Explorer. Be aware that no new versions of the early releases
have been updated to fix bugs. This implies that the beta versions are more secure. Of course, beta
versions may also have new bugs of their own.

Recall that although the Princeton Class Loader attack has been fixed for most versions of Netscape
(see Applets Running Wild in Chapter 5), the problem persists in version 2.0. The same thing goes
for the Jumping-the-Firewall attack. Counting on an old version of a browser for your security is
probably not a good idea.

Use the next guideline to help determine which version of a browser you need to have. Then get it.

Keep a Lookout for Security Alerts

Every once in a while, check the latest security information on Java. This book's companion Web
site (www.securingjava.com) is quite current, but you should also keep an eye on the Sun
Microsystems official Java Security FAQ. Also, have someone in your organization (or someone in
your group of friends) subscribe to the CERT Alert list.
If the CERT Coordination Center finds a security hole either particularly egregious or particularly
popular among crackers, they will warn the community at large. To date, CERT has issued two
security alerts about Java. Both were related to attacks found by the Princeton team. The alerts were
reprinted in Appendix B of Java Security: Hostile Applets, Holes, & Antidotes (the first edition of
this book), where you will also find information about signing up for online delivery of CERT
Alerts.

The CERT Alerts have the advantage of telling you the status of any security attacks and how to
avoid them. For Java, this includes information about which browser versions are susceptible to a
particular attack and which versions have been patched.

More Drastic Measures

There are, of course, a couple of drastic measures that can be applied to lessen Java security risks.
These both involve not using Java. These drastic alternatives are not called for unless your
information is ultra-critical. If you determine that the risks described are simply too great for you to
bear, you can implement these strategies.

Stopping Java at the Firewall


Many firewall vendors claim to have added the capability of stopping Java applets at the gate by
using special firewall products. Some firewalls block Java applets by parsing a Web page's HTML
code as it arrives. This action is typically performed by the HTTP proxy. HTML-parsing capability
makes a firewall capable of blocking any HTML-related tag. Thus, the relevant proxy can cut out
Java (by looking for the <APPLET> tag), JavaScript, and even old HTML 2.0 tags that are deemed
disallowed during setup. This strategy is useful only for HTML coming in through the proxy via
HTTP. That means that applets coming into the system in other ways (for example, through SSL or
via ftp) can still get through.

Another solution to the same problem might be to try to stop all files with a .class extension at the
gate. Still another would be to scan all incoming binaries for the magic number each applet is
required to have. The end result is the same: No applets are allowed past the firewall.

Research results show that stopping mobile code is much more difficult than it may seem at first
glance. An excellent technical paper entitled Blocking Java Applets at the Firewall by David
Martin (Boston University), S. Rajagopalan (Bellcore), and Avi Rubin (ATT Research) can be
downloaded from www.cs.bu.edu/techreports/96-026-java-firewalls.ps.Z (or see the Java Security
Hotlist).

Stopping all applets at the firewall is a radical solution that gives up all the good things that Java
has to offer. It may be a viable alternative for machines that require limited Web access but are
considered too important to expose to any risk beyond that. Unfortunately, this strategy resembles
throwing the baby out with the bath water.
Hiding Under the Bed
Believe it or not, there are strategies even more paranoid than blocking applets at the firewall.
Some sites may have such sensitive information that they decide they can't afford to take any risks
at all. These sites protect themselves by not even connecting to the Internet in the first place. Java
risk is thoroughly countered by this strategy, but all of the benefits that come with being connected
to the Internet are given up in return. Java's power and flexibility may still find room on such a
company's intranet, but there will certainly be fewer uses for Java.

CHAPTER SECTION:3

Section 3 -- Guidelines Are Never Perfect


The guidelines included in this chapter can help you develop more secure code and use Java more
securely, but in the end, they can't guarantee anything about security. Following the two sets of
guidelines is probably a good idea, but risks will always remain, no matter how vigilant you are.
Plan accordingly.

If you are doing business on the Net, be particularly careful. The stakes change significantly when
it comes to electronic commerce. An appropriate introduction to the security dangers inherent in e-
commerce is Anup Ghosh's book, E-Commerce Security [Ghosh, 1998].

Whatever you do, pay close attention to the tradeoffs between functionality and risk. We make
security and safety tradeoffs every day when we leave the house, and often they are very
appropriate. Making these decisions is more reasonable when you know about potential pitfalls.

Java Card Security


How Smart Cards and Java Mix
We emphasize throughout this book that Java is much more than simply a programming language.
This chapter takes that lesson to heart and discusses Java's role in a technology destined to become
part of all our lives in the near future-smart cards. In light of its history as a platform for embedded
devices, it's a bit ironic, but not surprising, that in one of its many forms, Java is coming full circle
back to its embedded roots. Smart cards provide an important enabling technology for e-commerce.
They are integral building blocks in many systems currently on the drawing board and a few
systems already in service. Java can help make smart cards more accessible to developers and
business people by providing a well-understood, familiar environment.
One of the barriers blocking the adoption of smart cards throughout the world has been the lack of a
common platform on which to develop applications. Java can help dismantle that barrier, opening a
world of potential development opportunities to Java developers and cutting-edge organizations.

This chapter answers a number of questions about Java-based smart cards, including:

• What is a smart card?


• Why put Java on a smart card?
• How can Java fit on a card?
• How secure are smart cards?
• What role can smart cards play in e-commerce systems?
• How does the use of Java impact smart card security?

These questions are important ones to ask since smart cards have already started appearing in
consumer wallets, especially in Europe.

Chapter Eight Sections

1. Java Security Goes Both Ways


2. What Is a Smart Card?
3. Why Put Java on a Smart Card?
4. How Can Java Fit on a Card?
5. How Secure Are Smart Cards?
6. What Role Can Smart Cards Play in E-Commerce Systems?
7. How Does the Use of Java Impact Smart Card Security?
8. Managing Risks

Java Card Security: How Smart Cards and Java Mix


CHAPTER SECTION:1

Section 1 -- Java Security Goes Both Ways


There are a large and growing number of Java systems running the gamut from Java gizmos such as
Java rings, through smart cards with built-in Java interpreters (the subject of this chapter), to
complete Java Development Kits and Integrated Development Environments (IDEs). Java is
simultaneously making in-roads on many fronts. In distributed systems, Java-based servers and
servlets are becoming as common as Java clients. As with any platform meant to interact in a
networked world, there are security concerns with each flavor of Java.
These concerns take on a new urgency when it comes to e-commerce. When electronic blips are
money, the stakes change considerably. It may be an inconvenience to lose a Web server that
amounts to fancy brochureware; it is something else entirely if the Web server and its associated
backend perform all customer transactions.

The security concerns raised by e-commerce are a large enough topic in their own right that there is
no way we can do them justice here. Of course, because Java is commonly used at all levels in e-
commerce systems, the risks we identify have serious e-commerce implications. This is especially
true for Java cards.

Counterintuitively, Java is both growing and shrinking at the same time. On one hand, the JDK,
now up to Java 2, is doubling in size with each major release. Just to complicate matters, at the
same time as the explosive growth of the code base is occurring, the security architecture is
undergoing major reorganization. Chapter 2, "The Base Java Security Model: The Original Applet
Sandbox," and Chapter 3, "Beyond the Sandbox: Signed Code and Java 2," detail the new model.
On the other hand, embedded Java systems like Card Java 2.x strip Java functionality down to bare
bones. The security model is not immune to this effect and has been deeply affected by Java's
migration to smart cards.

These two diverse directions both have important security implications. Java 2 involves
fundamental changes to the Java security model as the Java sandbox metamorphoses itself into a
trust-based system built on code signing. Card Java 2.x removes much of the sandbox, leaving
smart card applets more room to misbehave.

CHAPTER SECTION:2

Section 2 -- What Is a Smart Card?


A smart card looks just like a credit card, only with a chip embedded in 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. Figure 8.1 shows a Visa smart card.

Figure 8.1 Visa has been active in the


development of the Java Card.

Visa's Open Platform classes interact with


Java Card to provide a secure framework
for which to develop smart card
applications. (This figure used by
permission from Visa.)

A smart card chip is actually a complete little computer with nonvolatile 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 many different uses for smart cards. Smart cards can serve as:

• Security cards that are able to identify the carrier using advanced authentication algorithms
and can safely store secrets like private keys
• Electronic wallet cards that use several different approaches to store value and provide a
kind of electronic cash
• Transaction cards that take over the role once played by the magnetic stripe commonly
found on the back of credit cards
• Processor cards that carry out proprietary calculations in a black box fashion
• Memory cards that act as highly portable databases
• Cards with Virtual Machines that run Java applets

Unlike traditional computers, smart cards are not delivered with a built-in power supply, a
keyboard, or a display device. That means smart cards require a terminal in order to work. Such a
terminal is usually called a smart card reader or a card acceptance device (CAD). Some of these
readers can be quite small and provide limited capabilities. For example, one CAD popular in
Europe among users of stored-value telephone cards is not much bigger than a smart card itself.
This CAD's sole purpose is to display the balance of money stored on the card. (Having a trusted
device with which to query a card you own is important for consumer peace of mind, especially
when spending money at businesses that are of questionable trustworthiness. Plus, it's nice to be
able to verify that transactions, like adding money to a card, actually happen properly.)

CADs are also present at merchant sites (usually in the form of point-of-sale devices) and at issuing
banks. These CADs may look much like a typical point-of-sale credit card processor, with a small
display screen and a few buttons for answering queries or entering PINs. In similar fashion to
today's ATMs, a smart card is inserted directly into the reader. The reader then provides power to
the card and sets it up to receive software commands. Although a CAD of this sort may look like an
ATM on the outside, a smart card is much more powerful than an ATM card or a mag-stripe credit
card. These latter cards store only a few kilobytes of information on the magnetic stripe and are not
capable of performing computations. Smart cards can store many megabytes of information and
carry out sophisticated cryptographic calculations.

Another sort of CAD more commonly encountered by developers is a CAD connected directly to
the serial port of a standard PC. Using development environments similar to those used in normal
application development, programmers can create code for smart cards, download it into a
prototyping card, and in this way create new smart card applications. Most major smart card
vendors have proprietary Java development environments: Gemplus offers
the GemXpresso environment; Schlumberger offers Cyberflex.

There are many custom command sets for smart cards. In fact, there are so many custom
environments and they are so specialized that the number of smart card programmers is very small.
Considering that smart cards are traditionally programmed in platform-specific assembly languages
and that each vendor has a different language, the small number of programmers should come as
little surprise.

The situation could be worse, of course. At least all of the major vendors conform to a set of
common communications standards. The ISO 7816 specifications provide at least some common
ground and ensure that smart cards have similar terminal pin-outs, accept standard protocol
messages, and store some information in databases of common design [ISO7816, 1987]. ISO 7816
defines some commands in great detail and lays out communication protocols used by smart cards.
Because of the existence of 7816, a smart card made by, say, Gemplus, can interface with a CAD
meant for, say, Schlumberger cards.

Smart cards have long been associated with security since they provide a partial solution to the
need for personal identification and nonrepudiation. Because smart cards provide more nonvolatile
storage than other highly portable devices, they make ideal storage compartments for digital
identities. They can also compute hashes, session keys, digital signatures, and MACs right on the
card. To the extent that a card is tamper resistant (something we will discuss further later in this
chapter), it can be used to store important secrets such as DES keys or RSA private keys.

CHAPTER SECTION:3

Section 3 -- Why Put Java on a Smart Card?


As we mentioned earlier, one obstacle blocking widespread use of smart cards in U.S. 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.x (seewww.javasoft.com/products/javacard/index.html).

The problem of multiple, noninteroperable platforms is not limited to smart cards, of course. A
major part of Java's appeal is that it was designed as a cross-platform solution. Developers have
always wanted a solution to the platform problem (other than the adoption of one single proprietary
platform controlled by a monopoly). Java is one good way of addressing the platform problem on
smart cards.
A Java card is a smart card that is able to execute Java byte code, similar to the way Java-enabled
browsers can. But standard Java with all of its libraries (especially in the Java 2 guise) is far too big
to fit on a smart card. A solution to this problem is to create a stripped-down flavor of Java. Card
Java is just such a flavor. It's based on a subset of the Java API plus some special-purpose card
commands.

Besides providing developers with a more familiar development environment, Card Java also
allows smart cards to have multiple applications on them. For the most part, existing smart card
products (especially in the financial arena) have only one application per card. This application is
automatically invoked when power is provided to the card or the card is otherwise reset. The one-
application-per-card paradigm doesn't scale well, to say the least. Who wants to carry 20 credit
cards around? Card Java can solve this problem by allowing multiple applications, potentially
written by different organizations, to exist on the same card. The idea of multiple applications
running on the same VM by potential competitors raises a number of security issues, which we
address later in the chapter.

CHAPTER SECTION:4

Section 4 -- How Can Java Fit on a Card?


Even a stripped-down version of Java and its accompanying VM requires a fair amount of
computational power in order to work. 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 Java 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. Gemplus and
Schlumberger both distribute commercial Card Java environments. Of course, the memory and
interface constraints of smart cards deeply affect programming style, testing concerns, and other
aspects of program development.

Card Java has many features familiar to Java developers, especially those developers familiar with
JDK 1.0.2. Card Java includes:

• Packages
• Dynamic object creation
• Virtual methods
• Interfaces
• Exceptions

Elements of Java that are not supported include:

• Dynamic class loading


• A security manager
• Threads
• Cloning
• Garbage collection
• Finalization

A number of limitations are also imposed on runtime card application (or cardlet) behavior. The
"Java Card 2.0 Language Subset and Virtual Machine Specification," a Sun Microsystems
document available on the Web at www.javasoft.com/products/javacard/index.html, describes
the smart card version of Java in more detail.

In the current Card Java paradigm, applets live on a card forever once they are installed during a
process commonly called personalization. More specifically, although an applet's byte code may
stay on the card forever once it is masked onto a card, an applet can be marked as unavailable and
thus be permanently disabled. All applets that will be used on a card are installed at one time,
before the card is issued to a consumer. These applets are selected and deselected 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. The JCRE is in
this sense the card executive, receiving messages (known as APDUs) on the input lines and
carrying out the appropriate activities such as selecting an applet. Only one applet can run at a time
in current Card Java implementations.

Future versions of Card Java are likely to allow applets to be loaded onto an existing card even
after it has been issued to a consumer (much the same way that applet code is loaded into a
browser's VM). This introduces a number of security risks, including the risk that downloaded
applet code will behave maliciously (in the manner of the hostile applets detailed in Chapter 4,
"Malicious Applets: Avoiding a Common Nuisance," andChapter 5, "Attack Applets: Exploiting
Holes in the Security Model") and the risk that poorly engineered code will disable or otherwise
break the platform. Unlike VMs inside browsers, a smart card VM is not quite as easy to restart,
nor are the security mechanisms as rigorous.

By this point, it should be clear that Card Java has important security implications. These
implications take on even more importance when smart cards are used in e-commerce systems.

CHAPTER SECTION:5

Section 5 -- How Secure Are Smart Cards?


Before we dig into the security implications raised by putting a Java VM on a smart card, we need
to address the issue of basic smart card platform security. Smart cards are funny things. Depending
on how they're used, smart cards can sometimes be meant to keep secrets from the very people who
carry them around and use them. Consider, for example, a smart card that stores monetary value in
an internal register. If the card user can figure out a way to change the value of the register outside
of traditional means, he or she might be able to mint money! Smart cards like this make tempting
targets for bad guys.

Because smart cards are often used in security-critical situations, they have undergone a fair
amount of scrutiny from security researchers. Two main results are worth considering before we
get into security issues specific to Java: 1) the terminal problem, and 2) physical attacks on the
card.

The Terminal Problem

Smart cards need a way to interact with their users. Since there is no built-in display capability in
most cards, the CAD must take on this responsibility. Any display used during critical transactions,
such as transferring money, needs to have two properties: the display must be trustworthy, and it
must be unspoofable. Making sure a terminal presents proper and trustworthy information to a user
is known as the terminal problem.

The terminal problem is really a trust issue. How is a card user to be sure that the card is doing
what it is supposed to be doing during a transaction? How can a card user check to see whether
account balances (for example) have been properly debited or credited? The problem is that cards
are very much black boxes.

Many systems now on the drawing board include the use of personal computers as client-side
CADs. Consumers will use a PC to interact with the smart card and to address the concerns raised
by the terminal problem. The problem is that PCs are notoriously insecure, especially when they're
used to exchange lots of documents and programs, as most consumers do. If you use your computer
this way, you're taking on a great deal of risk.

One direct consequence of PC untrustworthiness is a PC's impotence relative to the terminal


problem. If your PC can't be trusted, how can you believe that what it is telling you on behalf of
your smart card is correct? In fact, one excellent reason for using smart cards at all is that PCs can't
be trusted. The reasoning goes that it is better to store secrets like PINs, sensitive personal data, and
private keys on a smart card than on a PC. That way, if the PC is compromised, your secrets can't
be so easily stolen.

However, this leaves us with the terminal problem. A scenario can make this more concrete.
Imagine that someone has tampered with your Web browser either by hacking into your PC or by
tampering with the Web browser executable before you downloaded it. Now clearly you can't trust
the browser not to steal or rewrite data on the way from your smart card to you. Some things that
might happen are:

• The smart card requires a PIN before it can be used. Through a browser interface, you are
queried for your PIN (which you faithfully enter). The corrupted browser sees the PIN go by
and stores it for later illicit use.
• The PC is used as a listening post in order to carry out capture/replay attacks against the
smart card (these kinds of attack often work against cryptographic protocols unless the
protocols are carefully designed to address this problem).
• The PC steals the private key off the smart card and is able to "legally" represent you by
digital signature.

What is needed is a trusted display. Some researchers have suggested that PDAs such as 3Com
PalmPilots might serve as trusted displays. The idea is that the PDA can interact directly with the
user during security-critical operations like PIN input. In fact, the PDA can replace the smart card
entirely since it can easily carry out all the required computations. (PDAs are probably too
unwieldy for this idea. It's much easier to slide a smart card into your wallet than a PalmPilot.)

Unfortunately, there is not much reason to trust a PalmPilot much more than a PC these days. The
problem is that newer PalmPilots and other PDAs are designed to network with PCs directly
(sometimes even using a TCP/IP stack). That's good news if you want to transfer data to and from
your PDA, but it's risky. Just like a PC, a PalmPilot is probably insecure if you frequently
download programs onto it. Crackers are currently devising hacks that work against PalmPilots.

In the end, we're stuck with the terminal problem. As smart cards move into more widespread use
on PCs, PC-based interfaces will be especially susceptible to this problem. An insecure Windows
95 OS in concert with a Web browser should not be trusted to display critical information to a
smart card user. A PDA might do the trick, but is likely to carry similar risks.

Physical Attacks on Smart Cards

The most obvious and direct attack on a smart card is a physical attack on the card itself. In the case
of a stored-value card, this sort of attack may even be carried out by the owner of a card. Physical
attacks attempt to reverse engineer the card and determine the secret key(s). Such attacks have been
demonstrated in practice against commercial secure smart card chips, most notably by three groups
of researchers: Dan Boneh, Richard DeMillo, and Richard Lipton of Bellcore; Ross Anderson of
Cambridge and Marcus Kuhn of Purdue; and Paul Kocher and colleagues of Cryptography
Research, Inc.

Boneh, DeMillo, and Lipton


Boneh, DeMillo, and Lipton, three Bellcore researchers, published a paper called On the
Importance of Checking Cryptographic Protocols for Faults in which they pointed out that an
adversary who can introduce computational errors into a smart card can deduce the values of
cryptographic keys hidden in the smart card [Boneh, et al., 1997]. The surprising part is that an
attacker can do this even without precisely controlling the nature of the errors or even the exact
timing of the errors. By comparing the result of an erroneous encryption with the result of a correct
encryption of the same data, the attacker can learn something about the correct encryption key. By
doing enough of these comparisons, the attacker can learn enough information to deduce the entire
encryption key.

How does the attacker introduce errors? There are plenty of ways. The attacker can subject the
smart card to fluctuations in temperature, input voltage, or clock rate; point a radiation source at the
card; or hit the card with a rubber mallet. Anything that is likely to cause voltages inside the card to
fluctuate will do.

Biham and Shamir later generalized this attack with a technique called Differential Fault Analysis,
which works against a wide range of cryptographic algorithms. The upshot of all this is that unless
a smart card cryptography mechanism is very carefully designed, any secret keys stored inside the
card might be extracted by a determined attacker.

Anderson and Kuhn


In a paper entitled Tamper Resistance - A Cautionary Note
(www.cl.cam.ac.uk /users/cm213/Publications/tamper.html), Anderson and Kuhn point out that
"smart cards are broken routinely" and to the extent that their secure use requires tamper resistance,
smart cards "should be treated with circumspection." The paper describes a number of smart card
attacks, many of which can be carried out by amateur attackers with very limited resources
[Anderson and Kuhn, 1996]. Attacks described include voltage manipulation, temperature
manipulation, chip removal (for easier probing), UV light attacks, and microprobing.

More sophisticated attacks requiring professional equipment and materials involve uncovering the
layers of a chip by etching, discerning chip behavior by advanced infrared probing, and reverse-
engineering chip logic. The somewhat gloomy conclusion is that, at best, chip designers can only
impose costs and delays on attackers, never providing guaranteed security. Many businesses that
rely on smart card security realize this and do all they can to manage the risks prudently. Users
should do the same.

Some caveats: the Anderson and Kuhn work is somewhat dated and is based on attacks carried out
in the lab against conventional micro-controllers, which are usually much simpler than today's
smart cards. Micro-controllers provide a great deal of open access to potential attackers since they
are meant to be interactively programmed. For example, micro-controllers often provide an
interface for external memory; generally speaking, smart cards don't have this feature. Thus they
provide less of a beachhead for attacks.

Of course Java complicates this line of reasoning somewhat. Card Java provides a platform that is
meant to be programmable in the sense that applets may be loaded onto a smart card after it is
issued. Mechanisms for loading new code onto a smart card provide a good starting point for an
attacker.

Differential Power Analysis


In 1998, Researchers at Cryptography Research, Inc., led by Paul Kocher, publicly announced a
new set of attacks against smart cards called Differential Power Analysis (DPA). DPA can be
carried out successfully against most smart cards currently in production.
DPA is a complicated attack that relies on statistical inferences drawn on power consumption data
measured during smart card computation. The equipment required to perform DPA is simple: a
modified smart card reader and some off-the-shelf PCs. The algorithm itself is quite complex, but
details have been widely published.

Chips inside a smart card use different amounts of power to perform different operations. By
hooking a card up to an oscilloscope, a pattern of power consumption can be measured. Particular
computations create particular patterns of spikes in power consumption. Careful analysis of the
peaks in a power consumption pattern can lead to the discovery of information about secret keys
used during cryptographic computations. Sometimes the analysis is straightforward enough that a
single transaction provides sufficient data to steal a key. More often, thousands of transactions are
required. The types of sensitive information that can leak include PINs and private cryptographic
keys. Figure 8.2 is a conceptual diagram of DPA.

Figure 8.2 Differential Power Analysis.

Paul Kocher and his colleagues at Cryptography Research


devised a monitoring attack on smart cards based on
information leaked through card power consumption. This
figure used by permission from Cryptography Research.

Possible solutions include masking power consumption with digital noise or throwing random
calculations into the mix. Another potential solution is randomizing the order of card computations
so that in the end, the same computation is performed using different patterns of primitives. All of
these potential technological solutions are ways to mask the giveaway patterns in the power
consumption of the card.

DPA is actually a variation on an earlier attack discovered by Kocher. The earlier attack exploited
the fact that some operations require different amounts of time to finish, depending on which values
they are computing. In the same way that DPA allows an attacker to piece together key information
based on variations in power consumption, Kocher's timing attack allows an attacker to piece
together a key based on variations in the amount of computing time required to encrypt various
values.

One thing to note is that legitimate users of smart cards don't have to worry too much about DPA or
timing attacks, because the attack requires physical access to the card itself. Unless you lose your
card or insert it directly into an attacker's machine, there is not much threat that your card itself will
be cracked. The main risk that DPA presents is to companies that must concern themselves with
widespread fraud of the sort carried out by organized crime.
The best approach is to assume information will leak from a smart card and design systems in such
a way that they remain secure even in the face of leaking information. An approach of this sort may
preclude smart card systems designed to do all processing offline without a centralized
clearinghouse.

Detailed technical information about DPA can be found on the Web


at www.cryptography.com/dpa/technical/index.html.

No Free Lunch

It should not be surprising that smart cards have their own set of risks- 100-percent security is an
unattainable goal. Once again, risk management and defensive design are optimal strategies.

CHAPTER SECTION:6

Section 6 -- What Role Can Smart Cards Play in E-


Commerce Systems?
E-commerce means different things to different people. Some people, for example, limit the
meaning of e-commerce to commerce conducted over the Internet and the Web. For the purposes of
this chapter, we're going to use the term more widely. So, by e-commerce, we mean everything
from electronic business-to-business traffic (for example, Electronic Data Interchange), through
Internet-based systems, to any system in which money is represented as bits. Under this admittedly
overly broad definition, almost the entire economy is touched by some aspect of e-commerce.
We're intentionally invoking this broad definition to emphasize the utility of smart cards for
transacting business.

Smart cards are seemingly 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 (or at least someone who knows the right secrets). In addition,
smart cards can store value. Card carriers can decide with whom to share data and with whom to
transact business 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 consequences 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 he or she can mint electronic cash. In
light of the physical attacks sketched earlier in the chapter, this is a serious problem.

Multiple-application smart cards like the Java Card should directly impact the marketability of
smart card technology for e-commerce. When a single card can replace the many cards most
consumers carry around today, people are likely to want it. Imagine a single card that both holds
personal information (such as driver's license, social security, medical information, auto insurance,
voter registration, workplace ID, Web site passwords, keys for making digital signatures and
encrypting data) and also provides multiple functions (working as a phone card, a charge card for a
store, a video rental credit tracker, a credit card, a debit card, and an electronic cash repository).

Leading Web vendors like Netscape are developing APIs for smart card interfaces. The idea is to
use a smart card to store cryptographic data for use with existing protocols such as SSL. This will
allow Netscape users to interact over the Web with a well-understood (and widely accepted)
protocol. Microsoft is also building smart card interfaces into its products.

The first use of smart cards for e-commerce is likely to be as a key/identity repository. In this case,
smart cards act as highly portable hardware tokens that can be uniquely identified. Smart cards can
store personal digital certificates for use with the SET protocol and other authentication-based
protocols [Ghosh, 1998]. This could make it possible to carry out Web-based commerce on Internet
kiosk systems of the sort occasionally found in airports and coffee shops.

CHAPTER SECTION:7

Section 7 -- How Does the Use of Java Impact Smart


Card Security?
Much of the base security model found in Java's JDK 1.0.2 (and explained in Chapter 2) is not
present in Card Java. For example, Card Java has no Security Manager class. This means the whole
concept of security for Card Java applets is significantly different from the case of applets running
on a standard browser-based VM. Fortunately, some of Card Java's "missing parts" may actually
enhance the security situation; unfortunately, some of the changes introduce new security risks.

How Card Java Lessens Security Risks

One of the most difficult problems in Java security is figuring out how to preserve type safety,
while at the same time allowing dynamic class loading (see Chapter 2). If you can confuse the VM
about the types of objects it is manipulating, you can break the security model. In its current form,
Card Java takes care of this problem by removing dynamic class loading, making type safety easier
to enforce. Class loading has always been problematic in Java and has introduced a number of
serious security holes. Because it has no dynamic class loading, Card Java is less risky than regular
Java from this perspective.

Another constraint imposed by Card Java, lack of threading, makes security analysis of applet code
much easier than it is normally. Threading is difficult to implement properly and to use properly,
plus threading takes a fair amount of overhead in the VM and significantly impacts the VM
footprint. Although there can be multiple applets resident on the same smart card, Card Java
systems allow only one applet to be selected at a time. (The multiple-resident-applications concept
introduces risks of its own, which we address later.)

How Card Java Increases Security Risks

Lack of threads and the absence of dynamic class loading impact security in a positive way, but the
opposite effect can be seen with other Card Java features. In other words, the removal of some
features of Java (clearly intended to make possible the migration to Card Java) may introduce new
security problems. Risks that are introduced involve:

• Lack of garbage collection


• Exception propagation problems
• Multiple applications and applet firewalling
• Object-sharing loopholes
• Access to native code

These problems and their risks are discussed next.

Garbage collection is a good example of a feature whose absence has a security impact. Without a
system for freeing allocated memory, the problem of denial-of-service attacks is exacerbated. The
Card Java 2.0 specification does not require implementation of garbage collection on a card (in
fact, implementing garbage collection on a card would be no small feat). As a result, even subtle
programming errors can wreak havoc on a smart card. Memory leaks are a classic problem in
languages such as C++ that do not support automatic garbage collection [Hastings and Joyce,
1992]. (Note that the term memory leak refers to memory becoming full when objects are
inefficiently created and destroyed.) Since Card Java does not support garbage collection, logic
errors in applet code may over time exhaust free memory, rendering the card useless. This problem
is especially acute on cards with limited memory resources. Another closely related issue is the
"dangling reference" problem, in which a program frees memory even though it still has a pointer
to that memory. The problem is that the freed memory may be recycled for another purpose, and
then the old "dangling" pointer may be used, resulting in memory corruption. Unfortunately,
garbage collection is expensive in terms of both time and computational resources. Nevertheless,
the risk of memory leaks is real. The requirement to ensure that these kinds of errors do not occur
raises the need for extensive testing and analysis of Card Java applet code.

Exception propagation is an interesting issue for Card Java as well, since uncaught exceptions
could lead to a card being muted (disabled for normal use). The potentially fatal effect of
unhandled exceptions implies another significant exposure to unintended denial of service, once
again resulting from subtle programming errors. As with memory exhaustion, the requirement to
ensure that these kinds of errors do not occur raises the requirements for extensive testing and
analysis of the Card Java applet code.

Since Card Java allows multiple applications to be resident on the same smart card, there is a risk
of interapplication attacks. This risk is especially relevant in situations where applets may be
provided by competing vendors. Card Java defines applet firewalls meant to protect applets from
one another, although it is not exactly clear what technology underlies this terminology. It appears
that the main protections between applets are related to memory management; in particular, applets
are not allowed to access memory arbitrarily on the card. As a default, they can only get access to
objects that they own (which are mapped into card memory during applet installation). It is clear
that the memory protection mechanisms 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.

One feature that is in high demand in systems with multiple applications is object sharing. Card
Java 2.0 includes an object sharing approach that includes a loophole. The current approach is
described as follows:

The JCRE keeps track of the currently selected applet as well as the currently active applet. The
currently active applet value is referred to as the applet execution context. When a virtual method is
invoked on an object, the applet execution context is changed to correspond to the applet that owns
that object. When that method returns, the previous context is restored. Invocations of static
methods have no effect on the applet execution context. The applet execution context and sharing
status of an object together determine if access to an object is permissible [Sun Microsystems,
1997].
To illustrate the implications of this approach, consider an example with three applets named A, B,
and C as shown in Figure 8.3. Applet A shares an object it owns, x, with applet B. Applet B shares
an object it owns, y, with applet C. A virtual method in the object owned by B, called y.bar, and
shared with C invokes a virtual method in the object owned by A, called x.foo. Now if applet C is
selected, it has permission to invoke virtual methods of the object owned by applet B, including the
one that in turn invokes the virtual method in an object owned by applet A. In other words, C
indirectly invokes x.foo.

Figure 8.3 Sharing a virtual method with another applet is


tantamount to allowing that applet complete control over the
method.

The problem is illustrated here. Applet A may think it is sharing method


foo() only with Applet B, but Applet B can in turn share the method
(through method bar()) with Applet C. If A doesn't want to share with C,
this may be a problem.
PLEASE NOTE: In the above image, between Applets B and C should
read "share (y,C)."

In short, if virtual methods are used, granting an applet permission to access an object amounts to
also granting that applet the ability to export indirect access to that object to any other applet. This
has a clear weakening effect on any assurance about the protection of an object. For static methods,
such access is prohibited, since static methods do not change the applet context. Restricting virtual
methods in the same way that static methods are restricted may add some complexity to sharing
schemes. The upshot of this change would be that an applet will be forced to share the object
explicitly with all other applets that directly or indirectly need to be granted access permission.

By far the biggest risk presented in the design of Card Java is a potential ability for a vendor to add
and use Native methods on the platform. Obviously, this will compromise portability (applets that
use Native methods will not be automatically portable to other cards), and it may expose the card to
dangerous code that exists outside the applet firewalls. In fact, if Native methods are available, the
concept of firewalls deteriorates. Native code is not restricted by the JCRE mediation of access, and
misuse is possible.

The very real security concern is that an attack applet will make use of Native code to carry out
operations that would otherwise be stopped by the JCRE. When Native code executes, all bets are
off for the Java Virtual Machine and its protection mechanisms. Native code in applets completely
breaks the idea behind Java security. Attack applets are likely to make use of Native method calls.

The five new risks we have covered were introduced into Java with its transformation to Card Java.
Although Card Java certainly presents an intriguing new set of risks in terms of security, it does
nothing to address several important fundamental security concerns associated with smart cards,
including the problem of physical attacks and the terminal problem (both discussed earlier in the
chapter). It is important to keep in mind that the non-Java-related problems have a real impact on
card security and that they are not solved by Card Java.

CHAPTER SECTION:8

Section 8 -- Managing Risks


New functionality in the form of smart cards promises to help solve some tough, real-world
problems and address important security concerns. With new functionality, however, comes new
risks. The security dilemma remains: How much risk are you willing to take, and to what benefit?

The Future of Java Security:


Challenges Facing Mobile Code
Now that you have reached this chapter, you have learned many things about today's Java security
model. One of the key lessons emphasized throughout the book is that the current Java security
model is more complex than ever. This makes Java security both difficult to understand and
difficult to use securely. The people at Sun Microsystems and other Java licensees have gone to
great lengths to provide a secure platform for mobile code and a set of tools with which to build
security-critical applications. To some extent they have succeeded, as Java is more secure by far
than any other mobile code architecture. The security model is complex; however, there are many
things you can do to make your use of Java more secure. In addition, there are a number of
challenges that remain for Java.

Chapter Nine Sections

1. Lessons from the Trenches


2. Challenges for Secure Mobile Code
3. Software Assurance for Java
4. Should You Use Java?

The Future of Java Security: Challenges Facing Mobile


Code
CHAPTER SECTION:1

Section 1 -- Lessons from the Trenches


Java has evolved more quickly than any other computer language in widespread use. As the
language itself has evolved, so, too, has the security model. Here are some lessons we have learned
as we have watched Java's approach to security change.

Type safety alone is not security. Chapter 1, "Mobile Code and Security: Why Java Security Is
Important," and Chapter 2, "The Base Java Security Model: The Original Applet Sandbox," explain
why type safety is critical to Java's security model. Chapter 5, "Attack Applets: Exploiting Holes in
the Security Model," shows what happens when type safety is compromised. Java security is much
more than type safety, however. Think of type safety as the foundation of a complete Java security
solution. The foundation is essential, but much needs to be built on top of the foundation to make a
useful building.

Real security is more difficult than it sounds. Securing a system as complex as Java (not to
mention systems built on top of Java) is a nontrivial undertaking. Do not let slick marketing
convince you that building a secure system is easy. It isn't. Java provides a powerful set of tools
that can help you create a secure system, but simply using the tools is no guarantee of success. You
must use the tools wisely and closely scrutinize your system from a security perspective. If ever
there was a time to practice solid software engineering, design, and implementation of secure
systems, it is now.
It is impossible to separate implementation errors from design problems. Recurrent security
problems are a clue that something is wrong with the way in which a system is being developed.
These problems are introduced at many different stages of system design and implementation. In
today's fast-paced consumerware market, security assurance is often among the first things to go.
Specifications, if they exist, are vague. Sometimes they are silent on critical points, leaving the
implementor to play a guessing game. What may seem to be a simple security bug often indicates
deeper problems in the development process.

New features introduce new security holes. Java has grown by leaps and bounds. With each
major release of Java, a number of new serious security holes have been discovered. This is not
surprising. It is exceptionally difficult to get everything exactly right in a complex system, and
security demands nothing less. Java 2 introduces many powerful new features into Java. Security
holes are likely to be discovered in the implementation and design of these features.

New classes of attacks keep appearing. Security attacks can be likened to rushing floodwater;
sometimes the places they flow are surprising. It is not possible to anticipate all future security
attacks, mostly because security attacks break systems in novel ways. Do not rely on using only
historical data for security analysis and protection. There is much more to real security expertise
than being able to recount stories of past compromises.

Humans are an essential element to consider. No system as complex as the Java platform can be
used without lots of human intervention. Java 2 provides a perfect example of this fact. Without
proper policy creation and management, trust-based security models are exceptionally dangerous.
Proper use of advanced technologies requires forethought, planning, and careful management. A
big part of this management is anticipating what real users of a system are likely to do, as well as
what real attackers are likely to do.

These lessons are relevant to each and every security-critical, Java-based system. Creating a secure
system is hard work, but it is quite possible to come up with a system that will have reasonable
security in the trenches and address a majority of risks as well.

CHAPTER SECTION:2

Section 2 -- Challenges for Secure Mobile Code


Java has risen to meet many important challenges of mobile code security. That means Java is by
far the most security-aware of the many mobile code platforms. If you want to use mobile code and
you are concerned with security, use Java. Of course, things are not perfect and there are still some
open problems. Here are a number of remaining challenges that secure mobile code systems,
including Java, must face.

Denial of Service
We spent some time discussing denial of service in Chapter 4, "Malicious Applets: Avoiding a
Common Nuisance." As we said there, denial of service is a difficult problem for computer security
that has yet to be addressed, not just in Java, but all over the network infrastructure. Successful
denial-of-service attacks have been carried out against ISPs by exploiting weaknesses in TCP/IP,
the protocol that is the life blood of the Internet. Java is not immune to the denial of service
problem, either.

Denial of service can be more or less serious depending on where the problem manifests itself. On
one hand, if a hostile applet crashes one user's browser by popping thousands of large windows, not
much real harm is done. On the other hand, if a servlet crashes an enterprise Web server, real harm
occurs. Our Chapter 4 discussion focused primarily on the client side; however, Java is making
inroads in places where denial of service takes on more urgency as a problem. Server-side Java is
one example. Another example can be found in systems with built-in Java VMs like Oracle8 or HP
Printers. The implications of denial of service for these systems is much greater than in the client
case.

New forms of denial-of-service attacks will be made possible with complex client-server systems
like RMI that make extensive use of networking, synchronization, and locks. Denial of service in
such a system becomes as easy as holding a lock. Distributed system applications will need to
apply timeouts and other mechanisms to mitigate the risks of an uncooperative process.

Dealing with denial of service is not an easy task. Limiting resource allocation is one solution.
Perhaps future versions of Java will include policy elements that can specify resource limits on the
basis of identity. That way, constraints can be managed according to where code is coming from
and how much it is trusted. These sorts of hooks do not yet exist in the Java 2 model.

Understanding Code Signing

There are a number of myths about code signing. Here are some of the most egregious:

Myth: Signatures denote authorship. The only thing a signature really tells you is who signed the
code. From this piece of information, people infer a sense of vouching; that is, if a piece of code is
signed, the implication is that the signer somehow vouches for the code. Unless you trust the person
or organization who signed a piece of code, code signing gives you nothing. In the end, code-
signing schemes simply amount to technological wrappings around a human trust decision.

Myth: If a signer is honest, the code is secure. Clearly, since all that a signature tells you is who
signed the code, the signature says absolutely nothing about the code's security. Even the best-
intentioned signer can only give you an honest opinion about the code, and that opinion might not
be worth much if the signer isn't a technical expert. Certification schemes may begin to change the
way this works if there are well-known, competent organizations that choose to vouch for certain
code properties. These organizations can have signatures that count as validation stamps.
Myth: Signatures imply accountability/liability. The legal ramifications of digital signatures and
what they denote have yet to be tested in the courts. Given the state of software liability in the
industry, it is unlikely that a signature will carry much legal weight in terms of liability.

Assuming these myths are properly debunked, there are still some real barriers to trust models
based on digital signatures. One of the main problems that will deeply impact the adoption of
signing-based approaches is the lack of a public key infrastructure (PKI). Without some way of
quickly and easily validating a signature, the market is unlikely to embrace code signing quickly.
Adding to any lethargy in adoption caused by the poor state of the PKI is the equally poor state of
tools for managing digital identities and policies (see Chapter 6, "Securing Java: Improvements,
Solutions, and Snake Oil"). In particular, issues of certificate revocation and storage loom large.

Secure Distributed Programming

Distributed computing is still in its infancy. Complex systems like CORBA reflect this. Managing
trust, identity, and policy in a distributed system is much more difficult than doing so on a VM-by-
VM basis using, for example, Java 2. Standards are emerging slowly, and there is much confusion
in the market regarding competing systems. Choices include Java's RMI, CORBA (encompassing
both IIOP and IDL), and DCOM (or one of its many marketing identities).

Common to all of these approaches is the problem of complex identity, which is not well
understood. Figure 9.1 shows why the problem is difficult. In real distributed systems not only is
code mobile, but other functionality is, too. Interprocess communication across different machines
can get hairy fast. RMI may not be equipped to handle some of the challenges that trust models
entail.

Figure 9.1 The problem of complex identity.

In this example, Bob's applet, running on Alice's VM is


communicating (possibly using RMI) with Donna's applet,
running on Charlie's VM. Creating usable policies for
situations like these is not well understood.

Being a True Multiuser System

The Java VM is not currently a replacement for a multiuser operating system. Neither is JavaOS a
real multiuser operating system. JavaOS in its current instantiations is meant only to run a single
Java VM. Object sharing and process firewalling rely on this fact to work. There are many
problems to solve before the VM can serve as a true multiuser environment, and researchers are
just starting to address them.

Persistence, Linking, and Versioning

Systems in which objects can be serialized (think of it as freeze-drying a process) and reconstituted
elsewhere (thawed out) are susceptible to the "environment problem," and it is likely that security
holes will be discovered in these systems. The problem is that there is no guarantee that the
environment in which an object is thawed will be remotely similar to the one in which it was
frozen. This can be a problem if the object assumes (as almost all code does) that its environment
doesn't suddenly undergo drastic changes. This kind of problem can easily lead to type-safety
problems and security risks.

The problem can be related to Descartes' brain in a vat experiment. In that venerable thought
experiment, seventeenth-century philosopher Rene Descartes (of "I think, therefore I am" fame)
asked how it is that we know for certain that our perceived environment is really there-that we are
not simply a brain in a vat that is being fed all the right data by a malicious demon. The unfortunate
answer is that we can't ever be certain.

If you substitute serialized code for the brain and the environment for the vat (and the controlling
demon), you can get the idea. Deserialized software will never be in the position to probe its
environment in order to discover where it really is or whether its environment is telling it the truth.
This is problematic when it comes to security parameters and types. Actually, this analogy works
well for mobile code in general.

Design for Security

Java offers a number of tools with which secure systems can be built. Obviously, this does not
imply that all systems written in Java that make use of its security features will be secure.
Designing a secure system takes much foresight and demands rigorous assurance at all levels of the
process. Risk-based security testing can help.

The best security-assurance approach begins with a system specification. Given a detailed-enough
specification, a thorough risk analysis can identify potential vulnerabilities and point out areas of
the system with the greatest risk. Security risk analysis includes characterizing threats and attacks
and working out attack scenarios based on potential attacks. For example, a specification may
prove vulnerable to playback attacks (a common problem among systems originally designed for
use on proprietary networks), decompilation (in which mobile code secrets can be divulged), or
cryptanalysis attacks (in which things like weak data integrity hashes can lead to complete system
compromise).
Given a thorough risk analysis of a system specification, the next step is to create a plan for probing
system security through testing. Note that the kind of testing we are talking about here is not
functional testing; it is risk-based testing related directly to the risk analysis. Functional testing tells
you only if your system meets design specifications and produces the right output. Risk-based
security testing tells you whether your system can be compromised by security attack. Security
testing is at its heart a creative endeavor that is only as powerful as the risk analysis on which it is
based. As such, security testing is no guarantee of security, but it certainly beats not testing for
security at all. By following a proper test plan, actual testing can be carried out on a live system.

External analysis for security is a good idea. Note that the definition of external can vary. At the
very least, a security review should be performed by a different team (within the same
organization) from the design team. Designers tend to be too close to the system and tend to
overlook security problems, even if they understand security well. In any case, it is essential that
external reviewers have a strong body of security expertise and knowledge on which to draw.
Analysis by external security experts may be warranted as well, although only for truly security-
critical systems.

Systems that are designed expressly with security in mind usually turn out better than those that are
not. One of the worst approaches to take is to try to bolt security on the side of an existing system.
This is especially true of systems that have been fielded successfully on proprietary networks (or no
network at all) and that are migrating to the Internet.

CHAPTER SECTION:3

Section 3 -- Software Assurance for Java


Many organizations creating security-critical code, especially those in the electronic commerce
arena, have reputations to protect. Banks, for example, are reluctant to talk about security problems
for good reason-bank customers expect their bank to have excellent security. No one wants to play
Russian Roulette with his or her money (unless, of course, he or she is in Las Vegas). The same can
be said of any organization that has essential information assets to protect. Strong security
assurance practices can help.

The main objective behind software assurance is making sure that software does what it is
supposed to do. Software assurance encompasses more than just security. The same core idea-
making software behave-is deeply entwined with software reliability and software safety as well.

Sound Software Engineering

The most effective way to make sure your code behaves itself and does not introduce unnecessary
risk is to practice sound software engineering. Following a reasonable development process is a
good start. Begin with a problem-analysis phase that delivers both a set of requirements and a high-
level specification. Develop a software assurance plan simultaneously, so that problems are
identified and removed as early in the development life cycle as possible. The earlier you remove
problems, the more money you save.

Unfortunately, many software organizations creating software for the consumer market have poor
software development practices. Partly because of supercompressed development schedules,
specifications are rarely created. Without a specification, it is not possible to undertake a proper
security analysis.

Lack of a specification makes testing harder, too. There are two main constraints on testing in the
real world: 1) Exhaustive testing is known to be impossible (one of the fun results of theoretical
computer science) and 2) testing resources are always limited. This implies a direct need for ways
to optimize testing and get the most bang for your testing buck. In terms of security assurance, a
risk-based approach to testing is very cost effective. (See page 250.)

Automated tools and methodologies can help make software assurance more effective as well. The
days in which software analysis and testing was a black art are ending. A number of technologies-
for example, code coverage analysis-are entering the mainstream. Test tools can be just as effective
for security analysis as they are for more standard functional testing, especially when they are
wielded by security experts. Advanced assurance methodologies like software fault injection are
also maturing into powerful tools [Voas and McGraw, 1998].

The best approach to security is to design security into a system instead of adding it later. The
earlier in a development process that this can happen, the better. Risk analysis can be applied at all
levels of the software process, from requirements to finished process. Together, risk analysis and
testing for security result in a strong methodological approach.

CHAPTER SECTION:4

Section 4 -- Should You Use Java?


By now you should know the authors' answer to this important question: Java has much to offer,
but its use should be managed wisely. Although one might like a simpler answer, there is no way to
properly make Java-usage decisions on anything other than a case-by-case basis. The way to make
your own decision about Java is to start by assessing your risks. What could you lose? The next
step is to weigh the risks against the benefits. Is what you gain worth the potential loss?

Java has lots to offer. It's the most viable attempt so far to provide secure mobile code. Java is
deeply tied to the Web and comes with many advanced networking features. Java is powerful and
can be used to write full-fledged programs. Java is a more portable application language than any
other on the market today. Java includes advanced language features that all developers should
have access to. To top it off, Java is truly concerned with security.
On the other hand, by using Java, you are taking risks. Security can never be completely
guaranteed. Anyone who tells you it can is wrong. By connecting your computer to the Net at all,
you have decided to take some degree of risk. If you're willing to do that, then you should probably
be willing to use Java. Then again, there are things about Java that set it apart from other languages
and Internet services. For one thing, Java makes running someone else's untrusted code a normal
event!

The previous chapters have explained the current Java security model. Included in that discussion is
an analysis of the vulnerabilities found thus far. Hostile applets-both in the serious attack applet
guise and in the malicious applet guise-are a possibility that needs to be taken seriously. Security
research on Java will continue to expose problems in the future. The security community, working
closely with Sun Microsystems and the Java licensees, will make sure each new vulnerability is
quickly and properly patched.

In addition to discussing particular bugs in the current Java implementations, we have also
examined some more general concerns. Hopefully, most of these will be addressed in the
enhancements planned for the near future.

Finally, we have introduced some guidelines by which Java can be developed and used in a more
secure manner. If you are a Java developer (or a development leader), you can use our developer
guidelines to defend your code against security attacks. If you are a Java user (or part of an
enterprise built around Java), you can use our user guidelines to mitigate common risks.

We hope this book continues to prove a useful reference on Java security. Armed with this
information, you can make informed decisions regarding Java use for yourself and your business.

How to Sign Java Code


This tutorial was put together by John Viega and Tom O'Connor, both research associates at
Reliable Software Technologies. The four major sections each describe a separate vendor's code-
signing tools, including:

• Netscape's Object Signing


• Microsoft's Authenticode
• Sun's JDK 1.1 Code Signing
• Sun's Java 2 Code Signing

Some of the tools are tricky to figure out and use. This tutorial should help.

Before you dig into this tutorial, you should read Chapter 3, "Beyond the Sandbox: Signed Code
and Java 2," which discusses the major impact that signed code has on the Java security
architecture. Of special interest are the sections entitled Signed Code (see page 88) and Trust
(see page 92). The material there discusses the notions of trust, digital signatures, and certificate
authorities.

Appendix C Sections

1. Signing Classes with the Netscape Object Signing Tool


2. Signing Java Applets with Microsoft's Authenticode
3. Comparing Authenticode to Netscape Object Signing
4. Signing Code with Sun's JDK 1.1.x
5. Differences Between Netscape Object Signing and JDK 1.1.x javakey
6. Signing Code with Sun's Java 2
7. Differences between JDK 1.1 Code Signing and Java 2 Code Signing
8. In Conclusion

How to Sign Java Code


CHAPTER SECTION:1

Section 1 -- Signing Classes with the Netscape


Object Signing Tool
First in our tutorial, we'll take on Netscape's Object Signing Tool that can be used to sign Java code
(among other things). As in all of these systems, step one is obtaining an identity.

Getting a Certificate

Most digital signature schemes (PGP being a notable exception) involve the use of a Certificate
Authority (CA)-an organization that can vouch for someone's signature. After all, why trust code
just because it carries a signature? We need an objective third party to make sure people are who
they say they are. That means the first task in code signing is to obtain the proper credentials from a
CA. There are many CAs that can sell you certificates for signing Java code.

Netscape has links to CAs that support their Netscape Object Signing Tool
at https://certs.netscape.com. You can visit that page and pick a CA. Make sure that the CA you
choose provides a certificate that can be used to sign objects (some certificates can't).

VeriSign offers many flavors of Digital IDs. It heads up Netscape's list of CAs. We'll use VeriSign
as an example for obtaining a certificate; however, note that the process will differ depending on
the CA that you choose. To get a VeriSign certificate for Netscape Object Signing, select Software
Developer ID from the popup list at the top of VeriSign's homepage. Choose Netscape Object
Signing from the page that follows. There are two kinds of Software Developer IDs: a Class 2
Individual ID, and a Class 3 Commercial ID. The Class 2 ID costs $20 annually, while the Class 3
ID is a whopping $400 annually. For our purposes, we'll focus on Class 2 certificates. After
selecting Class 2, fill out the information form that asks who will be identified by the certificate
(making sure to include the all-important billing information).

VeriSign will do a limited background check on you before it will issue a certificate. For example,
it checks the data you enter against information publicly available on you through a credit check. If
your request for a certificate is accepted, VeriSign will email you a PIN and a URL that you can
use to retrieve the certificate. For an individual Class 2 certificate, the verification process is
usually close to instantaneous.

Once you receive that information, open the URL with Netscape Communicator and then enter
your PIN. Communicator will install the certificate in itself automatically. If you are using a shared
version of Communicator, someone may have already entered a password for the certificate
database that is stored in your browser. You will need this password before you can download and
install your certificate; otherwise, you will be prompted to enter a password for the certificate
database. Although this password is optional, it does prevent people from starting up your version
of Netscape and stealing your certificate by exporting it. You definitely don't want your certificate
stolen, because then other people can sign applets as you. Password information can be found in the
Security Info box of the Communicator menu, under Passwords.

If everything is successful, your certificate will appear in the Security Info box; check by going to
Yours under Certificates.

There are several things to know as you sign up for a certificate:

1. You do not want a Class 1 certificate, as it cannot be used to sign objects.


2. Use Netscape Communicator (4.x), even though you are allowed to request and download a
certificate using Netscape Navigator 3.x, because the support for certificates in 3.x is not as
good as it is in later browsers. For example, object signing tools may not be able to locate
your certificate inside Netscape 3.x. Also, you may not be able to export your certificate,
which is useful if you want to sign code from a machine other than the one from which you
originally downloaded the certificate.
3. Use the same browser on the same computer both to request and to retrieve the
certificate. If the browser is set up with multiple user profiles, make sure you use the same
user profile as well; otherwise, you will likely be unable to retrieve your certificate.
4. Finally, note that many versions of Netscape Communicator will be unable to verify your
certificate (assuming you got a VeriSign certificate). Unfortunately, information concerning
this problem seems to have disappeared from the Netscape Web site.

Exporting and Importing Certificates


It is a good idea to export your certificate to a file, just in case you install a new version of
Communicator over your old one. Doing so also allows your certificate to follow you to other
machines.

To export a certificate, bring up Communicator's Security Info dialog box. Select the certificate you
wish to export by clicking on its name. Then, click on the Export button. At this point, you may be
asked to enter the password that protects your local browser's certificate database. Next, you will be
asked to enter a password to protect the certificate data. This password is used to make sure that no
one can steal your certificate if he or she sees an exported copy of it somewhere (unless that person
is able to crack your password, so choose wisely!). To make sure you typed the password in
correctly, you will be asked to enter it again. Assuming you've entered the same password both
times, Netscape will prompt you for a filename, which it will use to store the certificate. Once you
enter the filename, you're finished exporting the certificate. You can copy that file to another
machine so you can sign code from there as well.

To import a certificate into a new browser, bring up Communicator's Security Info box. Click on
Yours, which is a subitem of Certificates. Press the button, Import a Certificate. If you have not
previously entered the password protecting the certificate database of the local copy of Netscape,
you will now be prompted to enter it. After you enter the correct password, a file dialog box will
come up; use it to select the file containing your certificate.

Once you have selected the file, you will be prompted for the password used to protect the
certificate, which is the password that you entered when you exported the certificate. At this point,
assuming all has gone well, you should get a dialog box indicating success.

Netscape Object Signing Tool 1.1

The Netscape Object Signing Tool is a command line program that creates digital signatures for
files. These signatures aren't stored in the files themselves; they're stored in the JAR file in which
you bundle your applet. Note that since digital signature information is transmitted in JAR files,
you must package your applets in a JAR file in order to sign them, even if they consist only of a
single class. The important syntax for using a JAR file with the HTML APPLET tag is:
<APPLET CODE="somefile.class" ARCHIVE="jarfile.jar">

where somefile.class is the class in the JAR file where execution should begin, and jarfile.jar is the
URL of the JAR file. The Netscape Object Signing Tool may be downloaded
from developer.netscape.com/software/signedobj/jarpack.html.

The tool is available for most operating systems. While, as of this writing, version 1.0 is still
available for download, we recommend that you use version 1.1.

After the download is complete, unpack the archive file in a directory. Included are three files:
readme.txt, license.txt, and signtool. To make signing objects easier, put the directory that contains
signtool in your PATH environment variable, as per your operating system. For example, a
Windows 95 user who unpacked the tool to C:\nos would run the following line (and then add it to
the autoexec.bat file):
PATH=%PATH%;C:\nos

Before attempting to sign anything, check to see if signtool is able to locate the certificate that will
be used to sign objects. Unix flavors of signtool look for certificates in the $HOME/.netscape. If
your local Netscape files are kept somewhere else, or if you are using the Win32 version, signtool
must be explicitly told the path to the certificates. This is done with the -d flag. On Win32, this path
is commonly c:\Program Files\Netscape\Users\name, where name is the name of your Netscape
Profile. To verify that your signing certificate was installed properly, runsigntool -l or, if
your certificate cannot be found,
signtool -d"<path to certificates>" -l

For example, if your certificates were stored in C:\nos, you would type:
signtool -d"C:\nos" -l

If your certificate still does not appear in the listing, verify that the certificate is installed in
Netscape properly. (See the instructions given earlier). Also check that the path to the Netscape .db
files was properly specified. If all else fails, check with Netscape and the issuing Certificate
Authority. Make note of the full name of your certificate as it appears in the listing, you will need
these data when it comes time to sign.

Create a directory in which to put all the class files for the applet you wish to sign. Once all the
class files that make up the applet are in the right place, the signtool program can create a signed
JAR file in one step.

Navigate into the directory containing the soon-to-be signed classes. To sign the classes and create
a JAR file in one step, issue the command:
signtool -d"" -k"" -e ".class" -Z myjar.jar .

If your Communicator Certificate Database is password protected, signtool will prompt for the
password before signing the classes. The "." at the end of the command should be the last thing to
appear. It specifies that the signing should begin in the current working directory. The signtool
command recursively signs files by default. To keep the tool from recursing through directories,
add --norecurse to the command line.

Here's a brief explanation of the flags used in the previous example, as well as some of the other
more useful flags for signing applets:

-k "certificate name": Specifies the certificate with which you would like to sign. This flag is
necessary when signing an applet. The certificate name should be the entire name of the
certificate as it appeared as the output of signtool -l. Since the certificate name is likely to have
spaces in it, make sure you place it in quotes; otherwise, the signing will fail.
-e".extension": Specifies the file extensions to sign. If you don't include this flag, the tool will sign
all files, as opposed to the preceding example, which uses this flag to sign .class files only.

-x"name": Allows you to sign all files except a particular file or directory. An example where this
might be useful is when you are using an untrusted library in your applet. You probably will not
want to vouch for code you did not write!

-Z"jarfile": Specifies the name of the JAR file to create. If you omit this option, you will have to
JAR everything up yourself.

When the JAR file is created, signtool can be used to test the validity of the signatures. This is done
by issuing the command:
signtool -d"<path to certificate>" -v myjar.jar

signtool will list the contents of the JAR and verify that they have been signed, and that they have
not been tampered with since the signature was created.

You may also check to see who signed the JAR file:
signtool -d"<path to certificate>" -w myjar.jar

signtool can be used to sign anything, not just Java files. In fact, it can extract JavaScript from
HTML files, and sign just the JavaScript; however, that functionality is outside the scope of this
tutorial. Documentation on signtool is available from Netscape
atdeveloper.netscape.com/docs/manuals/signedobj/signtool/.

Adding Capabilities to Your Classes

As Chapter 3 describes, signing a Java applet does much more than just allow people to verify that
you signed it. It can also give your applets the chance to step outside the Java sandbox. If your
applet has a digital signature vouching for it, then the applet may request special privileges, such as
accessing the file system. However, the user of the applet doesn't have to let your applet do what
you request just because you sign it.

The special privileges an applet can request are called capabilities by Netscape.1 Predictably, no
two browsers support flexible privileges in quite the same way, so privilege-management code will
only work with one browser. (So much for "write once, run anywhere"!) As a result, while
Netscape keeps its own internal version of these classes, in order to actually compile and test an
applet that can request them, you must download the library from developer.netscape.com/ docs/
manuals/signedobj/capsapi_classes.zip

Put the zip file in your CLASSPATH (or otherwise edit the CLASSPATH). Now you will be able
to develop code that requests extra privileges in Netscape. Note that you should not include these
classes with your applet; the Netscape browser running on the remote machine will use its internal
version of the classes.

The Capabilities library provides a class called the Privilege Manager that handles requests from
the program to turn on and off privileges. When the first request to enable a certain privilege is
made, the Privilege Manager prompts the browser's user, showing the certificate used to sign the
code requesting the privilege, and asking whether the privilege should be granted. See Figure C.1.
If the user agrees to grant the privilege, the privilege is granted for the lifetime of the applet.
However, once the applet has obtained a privilege, it can turn the privilege off and on at its
discretion.

Figure C.1 Netscape Navigator's Privilege Manager alerts a browser user with this window.

The dialog box explains which dangerous privileges have been requested and who is vouching for the applet
(through a digital signature). Clicking the "Remember this decision" is probably a bad idea.

To request a particular privilege to be enabled, you use the static method enablePrivilege() of class
netscape.security.PrivilegeManager. The method takes a single String argument, which is the name
of the privilege to enable. Some useful privileges include:

UniversalFileAccess: This privilege gives the applet the ability to access any file available
to the user. It will enable the applet to call most things in the java.io package related to file
manipulation. This privilege is a superset of other file manipulation privileges that may be
requested individually, such as UniversalFileRead, UniversalFileWrite, and UniversalFileDelete.

UniversalSendMail: This privilege allows the applet to send email on behalf of the user.
UniversalExitAccess: Allows the Java applet to shut down the Netscape browser.

UniversalExecAccess: Enables the applet to run programs already stored on the user's local
computer.

PrivateRegistryAccess: Grants access to application-specific portions of the computer's


registry (Win32 only).

There are many more privileges that an applet can request. For a full list, see the documentation for
the Capabilities API atdeveloper.netscape.com/docs/manuals/signedobj/capabilities/01cap.htm.

A call to enablePrivilege will throw an exception that the applet must catch if the user decides not
to grant the privilege specified in the call. Thus, the applet must be prepared to catch instances of
netscape.security.ForbiddenTargetException. Here's a sample applet called FirstTry.java that uses
enablePrivilege

import java.applet.*;
import java.awt.*;
import netscape.security.PrivilegeManager;
import netscape.security.ForbiddenTargetException;

public class FirstTry extends Applet {

private TextArea ta = new TextArea(10,100);

public void init() {


this.add(ta);
this.show();
try {
PrivilegeManager.enablePrivilege("UniversalFileRead");
ta.appendText("Read enabled!\n");
}
catch (ForbiddenTargetException fte) {
ta.appendText("Read not enabled.\n");
ta.appendText(fte.toString());
}
catch (Exception e) {
ta.appendText("Unexpected exception!\n");
}

public void paint(Graphics g) {

}
}

The FirstTry applet doesn't do anything with the privilege it asks for, even if it is granted. However,
it would be able to read any file, including the system password file on a Unix system, if it tried.
That could be considered an abuse of privilege. Another potentially bad thing this applet could do
would be to put the enablePrivilege call inside the paint method. Doing this will cause the browser
to continually prompt the user for permission every time paint is called, which will happen until
permission is granted, or until the browser is killed. Actually, the Netscape Grant/Deny window has
a checkbox that says "Remember this decision." Checking the deny box will make this pop-up
never appear again. The take home message is that signed applets can be hostile too.
When you enable a privilege, it does not have to stay enabled for the entire execution of the applet.
There are a couple of ways to turn privileges off (which is always a good idea). First, when the
method that calls enable Privilege returns, the privilege will automatically be disabled. As a result,
you should not use a helper method to enable a privilege, because once execution returns from that
method, the privilege will no longer be enabled. Second, you can call revert Privilege, which also
takes the name of a privilege as an argument. Finally, you can call disable Privilege, which turns
off a particular privilege. In no case will the granting of the privilege be revoked; the applet can
turn the privilege back on by simply calling enable Privilege again. To see an example of a signed
applet, surf to www.securingjava.com/dpig/netscape.html.

CHAPTER SECTION:2

Section 2 -- Signing Java Applets with Microsoft's


Authenticode
Next in our tutorial, we'll take on Microsoft's code-signing system for Java. It's a bit peculiar since
it does not interact with the JDK 1.1 or Java 2 security models in an intuitive fashion. As usual, step
one is securing an identity.

Getting an Authenticode Certificate

There are several ways to get a certificate for Microsoft Authenticode. One of the things you can do
is generate "test certificates," which allows you to try things out. We'll tell you how to do that in a
bit, if you just want to play around. However, if you plan on distributing any code, you're going to
want to get a real digital ID. This costs money. A number of vendors distribute certificates, one of
them being VeriSign, which we'll use in our examples.

To obtain a VeriSign certificate for Authenticode, point Internet Explorer


to digitalid.verisign.com/developer/ms_pick.htm.

Select a flavor of ID. For personal use, select a Class 2 ID. For business use, select Class 3.

You'll be given a form to fill out. Note that the personal Class 2 ID is $20, and you'll have to pay by
charge card. Once you submit the form, VeriSign will try to verify you are who you say you are,
mainly by running a credit check. Sometimes the credit check won't have up-to-date information,
so if you get rejected and you can remember the address for the last place you lived (which is the
most common problem), you might want to try it again using old information, pretending you never
moved. (Not that we condone this strategy, mind you.)

Once your data are approved, VeriSign will send you an email with instructions on picking up the
certificate. When downloading your certificate, two files will need to be saved: your private key
file, and your certificate file. You should probably save these files to a floppy disk instead of your
hard drive, so that someone can't just snag your certificate off your computer (although, without
knowing the password you use to protect your private key, snagging the files alone may not do a
bad guy much good). Remember the password used to protect the private key; it will be needed
when it comes time to sign code. For the sake of simplicity, we'll assume you saved your certificate
as a:\Cert.spc and your private key as a:\Key.pvc.

Getting the Signing Software

Before signing anything with the new certificates, download and install the Microsoft Java SDK.
It's located atwww.microsoft.com/msdownload/java/sdk/31f/SDK-JAVA.asp.

We'll assume you installed the Java SDK in the directory C:\SDK-Java.31.

All of the programs we're going to need for signing Java code live in C:\SDK-
Java.31\Bin\PackSign, so you should probably add that directory to your PATH. Under Windows
95/98, running the following command at the DOS prompt will fix up your PATH for the current
session:
PATH=%PATH%;C:\SDK-Java.31\Bin\PackSign

You can add that command to your autoexec.bat file to make the change persist through a reboot.

Cabinet Files

Unlike Netscape's Object Signing and Sun's signing tools (which work on JAR files), Authenticode
signing will only work on cabinet (CAB) files. There's nothing special about the CAB format; it's
just another way of archiving many files into one. However, it's the only archive format IE supports
for signing Java code.

Say we have an applet that consists of two files: file1.class and file2.class. We can create a CAB
file in the same directory by typing the following at the DOS prompt:
cabarc N test.cab file1.class file2.class

If there are no other class files in the directory, we can also type:
cabarc N test.cab *.class

Security Zones
In order to understand what we're doing when we sign a CAB file, we need to know a little
something about what an IE "security zone" is. By default, a security zone is a group of Web sites.
Each zone is assigned a security level, which may be Low, Medium, High, or Custom. We won't
cover Custom zones, except to say that they can implement arbitrary security policies. For more on
security zones, see Chapter 1, "Mobile Code and Security: Why Java Security Is Important."

There's a default zone called Trusted Sites, into which a user can put any server. All code from that
zone will be completely trusted (i.e., the zone has a Low security level). Similarly, there's a
Restricted Sites zone. Any sites the user puts in this zone will need explicit permission before they
can run anything "outside the sandbox." By default, most everything else falls into the Internet
zone, which is assigned a Medium security level. Code can run outside the Java sandbox in a very
limited manner. For example, code can use up to 1 megabyte of data on your hard drive by using
the API com.ms.io.clientstorage, which is included with Microsoft VMs only. (So much for "write
once, run anywhere"!) Unlike fully trusted applets, applets restricted to the Medium security level
should not otherwise be able to use your file system.

We're going to sign our cabinet file, requesting to run either with Medium or High privileges (we
can also request Low privileges, but since we'll always be allowed to run in the sandbox, doing so
is mainly useful only to show you vouch for the CAB file). If our code ends up in a Low security
zone, our code will always run without prompting the user for permission. If our code ends up in a
Medium security zone, then before code that requests Medium level privileges can run, the user
will be prompted as to whether to let the code run. If our code ends up in a High security zone, all
code that wants to run outside the sandbox will need to be approved through a dialog with the user.
See Figure C.2.
Figure C.2 The security warning dialog used by Microsoft Internet Explorer's Authenticode
system.

This dialog explains who has vouched for the code (by signing it) and what permissions are being
requested. Clicking "Always trust content from <user>" is probably a bad idea.

Signing CAB Files

To sign test.cab, we're going to use the signcode command, which is included in the Java SDK.
Here's a typical command line:
signcode -j JavaSign.dll -jp High -spc a:\Cert.spc -v a:\Key.pvk -n "My Applet" -i
http://www.mywebpage.com/ test.cab

The flags here are a bit arcane. If you want your CAB file to request permissions, the -j flag should
always be there, and take JavaSign.dll as a parameter, unless you're signing something other than
Java code (the same command can be used to sign ActiveX controls and other mobile code, too).
The -jp flag passes a parameter to the DLL. For Java signing, that's how we request High
privileges. The -spc flag and -v flag are used to specify the location of your certificate and private
key, respectively. The -n option needs to be present, and it specifies the name of the software,
which is displayed to the user before the user decides whether to run your code. The -i option
specifies where to go for more information about the product, which also gets displayed when the
user is prompted to give your code permission to run.

You can also "timestamp" your signature, so that after your certificate expires, your applet will still
work. However, doing so requires a timestamp server, which isn't covered here. For more
information on Authenticode for Java, visit www.microsoft.com/java/security.

To confirm that everything has worked properly so far, run the command:
chkjava test.cab

A window should appear similar to the one an end user will see when IE asks if the application
should be allowed to run.

Making Test Certificates

To avoid putting down some cash for a real certificate from a CA and still be able to play around
with Authenticode, you can make a test certificate. The first step is to create the certificate with the
command:
makecert -sk Key.pvk -n "CN=Your Name" Cert.cer
That command makes a certificate and a private key you can use in other applications, but it won't
work for code signing. To get it to work with code signing, convert it to a Software Publisher
Certificate (SPC) by typing:
cert2spc Cert.cer Cert.spc

When you're finished with that, you can use Key.pvk and Cert.spc for testing purposes in the same
way as if they came from a CA.

Special HTML Tag

When deploying a signed CAB file in an HTML page, a slight variation on the <APPLET> tag is
necessary. As with all applets, the name of the class that extends java.applet.Applet goes in the
CODE attribute. However, instead of putting the name of the CAB file in the ARCHIVE attribute
as is done with JAR files, CAB files signed with Authenticode are passed using the PARAM tag.
As an illustration, the tag to embed into a web page the signed applet "MyApplet" stored in
myapp.cab would look like:
<APPLET CODE="MyApplet.class"> <PARAM name="cabbase" VALUE="myapp.cab"></APPLET>

The named prameter "cabbase" is how Internet Explorer finds the CAB file containing the class
specified in the CODE attribute.

CHAPTER SECTION:3

Section 3 -- Comparing Authenticode to Netscape


Object Signing
Microsoft's Authenticode model is somewhat simpler than the Communicator model for the end
user. Assuming the user doesn't know anything about zones, lots of stuff runs without asking the
user for permission; the user is prompted only to approve code generally when the code requests
full access, and doesn't already have permission. Less interaction generally means less hassle for
the user. You can make more dialog boxes disappear if you check boxes like, "always trust code
from this person," and "always trust code from this site," which appear in the window that
announces that code is trying to gain permissions. However, spreading trust around so easily just to
avoid dialog boxes can have bad consequences.

Authenticode is also simpler for the developer. There's no need for calls to a Capabilities library,
meaning you can simply request an access level, as opposed to requesting a set of privileges.
However, Netscape is capable of finer-grained access control, which allows the applet to secure
only the resources it needs to run without a user feeling the need to give a program complete access
to the computer.
Another convenience of Authenticode over Object Signing is that the user only gets prompted at
most once per applet. Netscape prompts the user whenever new privileges are requested (which is
usually during execution). While the Netscape model is more intrusive, it does afford the user a bit
more control over what privilege is granted to an applet.

CHAPTER SECTION:4

Section 4 -- Signing Code with Sun's JDK 1.1.x


Sun makes its own set of signing tools. The tools have evolved along with the JDK. We'll briefly
cover both the JDK 1.1 tools and the Java 2 tools.

The JDK ships with a command-line tool called javakey. Its job is to manage a database of entities
(people, companies, etc.) and their public/private keys and certificates. It is also supposed to
generate and verify signatures for archive files; however, verification is not implemented as of JDK
1.1.7.

As Chapter 3 describes, an applet contained within a digitally signed JAR file is allowed to leave
the bounds of the Java sandbox under certain circumstances. In JDK 1.1, if a JAR is signed and the
user who has browsed to the Web site containing the applet has a policy stating that he or she trusts
the person who signed the JAR, the applet can do anything at all that Java code is capable of. For
example, it can read and write from the file system, start another process running on the computer
(outside of the browser), open a network connection to an arbitrary machine, or myriad other tasks
that applets are not normally allowed to do. In other words, trusted signed code under JDK 1.1 is as
powerful as Java application code from the JDK 1.0.2 days. Remember, under JDK 1.1, we're
operating under a black-and-white security model.

To get going with code signing in JDK 1.1, there are few things to gather. On the development side,
an applet that tries to perform actions that aren't normally allowed by the Java sandbox is needed
(or at least one that can be augmented to attempt such an action). The most rudimentary operation
that a signed applet can do that an unsigned applet can't do is read the user.name System property.
An example applet follows:

public class UserApplet extends java.applet.Applet {


public void init() {
String username = "user: ";
try {
username += System.getProperty("user.name");
} catch (SecurityException se) {
username += "cannot read";
}
showStatus(username);
}
}
A signed applet containing the preceding code (running in a browser of a user who trusts the entity
that signed the applet) will be able retrieve the name of the user running the applet and display it in
the status bar of the browser.

Once the applet to be signed and its containing Web page have been created, the class files that
contain the applet must be put into a JAR file. Even if the applet in question is only one class, it
must be placed inside a JAR file. It is not possible to sign standalone class files.

In order to sign Java code with javakey, a signing certificate needs to be created. Once this
certificate is created, it can be used to sign the JAR file and distributed to users who wish to allow
the signer's applet full access to their system.

Creating a Signing Certificate

A file called identitydb.obj stores all certificate information and lives in the directory specified by
the Java System Property value user.home. For Unix Java users, this value evaluates to $HOME.
For Win32 users, user.home can take a number of values. Different VMs assign the value of
user.home either to the USERPROFILE directory, to the HOMEDRIVE\HOMEPATH directory, or
when all else fails, to the value of the java.home System Property. To clear up any ambiguity, write
and run a simple Java containing the line:
System.out.println("user.home= " + System.getProperty("user.home"));

Regardless of user.home, the location of identitydb.obj can be set explicitly by adding an


identity.database entry to the java.security file that lives in the lib subdirectory of the java
installation, wherever that may be on the system.

First, the signer's identity must be created in the database. To create an identity, signername, that
will be able to sign objects, run the following command on the command line:
javakey cs signername true

Now that a signer has been created, that signer's public and private keys must be initialized. Keys
can be between 512- and 1024-bits long. To initialize public and private keys for a signer, run the
following command (where # is a number between 512 and 1024):
javakey gk signername DSA #

Higher numbers mean more security. We recommend always using 1024-bit keys. The parameter
DSA signifies the algorithm used to generate the keys. The JDK only comes with the DSA
algorithm by default.

To verify that all has gone well so far, run:


javakey ld
This command will list all the information in the current identity database. The entry for
signername should identify it as a trusted signer as well as noting that the public and private keys
have been initialized. The next step generates a certificate that will be valid for signing JAR files.
This is different from Netscape Object Signing in that there is no Certificate Authority involved.

First, a directives file must be created. The directives file is a Java Properties file that provides
values used during certificate generation. Here are the contents of an example directives file:

issuer.name=signername
subject.name=signername
subject.real.name=Sol S. Signer
subject.country=US
subject.org=Signing Corp
subject.org.unit=Development
start.date=22 Jul 1998
end.date=16 Aug 1999
serial.number=41

To generate the certificate once the directives file exists, run:


javakey gc directivesfile

To verify that the certificate was generated properly, run:


javakey ld

Look for the signername entry to have the subject.* information from the directives file listed.

Everything is now finally set for signing. The command actually used to sign a JAR file also
requires a directives file. This is a different directives file than the one used to generate the signing
certificate. The first directives file used to generate the certificate will no longer be needed, unless a
different certificate needs to be generated. The second kind of directives file is used whenever a
JAR gets signed, and should be kept handy. An example directives file for signing a JAR looks like
this:

signer=signername
# look at javakey ld for certificate numbers, should be 1
cert=1
# chain unsupported, must have as value=0
chain=0
# must be 8 characters or less and not conflict with any other
# digital signatures that could be inserted into the JAR
signature.file=anything
out.file=Signed.jar

Once the signingdirective file has been created, run the command:
javakey gs signingdirective UnsignedApplet.jar

Running this command will generate Signed.jar, which will be a signed version of
UnsignedApplet.jar. Putting Signed.jar in the ARCHIVE field of the APPLET tag in an HTML
page will cause a Java-enabled browser to bring the JAR file over the network and execute the
signed code contained within. For more information on javakey, the official Sun documentation for
Solaris can be found atjava.sun.com/products/jdk/1.1/docs/tooldocs/solaris/javakey.html.

The Win32 specific version can be found


at java.sun.com/products/jdk/1.1/docs/tooldocs/win32/javakey.html.

Testing the Signed Applet

Now that a signed applet exists, and it is embedded within a Web page, it's ready for testing before
release. Testing requires either appletviewer or a Web browser that knows how to validate JARs
signed by javakey and allows signed JARs to leave the sandbox. Unfortunately, neither of the two
major browsers (Netscape Communicator and Microsoft Internet Explorer) support javakey-signed
JARs.

HotJava and the appletviewer program that comes with the JDK can validate JARs signed by
javakey. They will allow signed applets out of the sandbox if the signature is valid and the policy
states that the user whose signature appears is trusted. Both of these programs search for the
identitydb.obj in the same manner that javakey does.

The problem is that no one should be surfing the Net with HotJava (too dangerous), and the
appletviewer cannot be used to browse the Internet. Since the VMs in Communicator and Internet
Explorer do not support javakey signing, in order run javakey-signed applets with those browsers,
users must download and install Sun's Java Plug-In.

Java Plug-In for Communicator and Internet Explorer

Java Plug-In can be used to run applets instead of the browser's default VM. The Java Plug-In can
be configured to use the most recent version of the Java Runtime Environment available from Sun.
When an applet is run through the Java Plug-In instead of the browser's default VM, javakey-
signed JARs can be verified and can step outside of the sandbox (if policy allows).

Users must download the Plug-In from Sun and install it on their system. The download page for
the Plug-In isjava.sun.com/products/plugin/index.html.

Applet developers also need to modify the HTML pages that contain their applets and modify the
<APPLET> tag. Applets that are in Web pages using the standard <APPLET> tag will still be run
by the browser's default page. The Plug-In will run applets only when it detects a different set of
HTML tags that specify an applet. Sun provides an application called HTMLConverter, which can
convert pages with the <APPLET> tag into pages containing tags that will launch applets using the
Plug-in. The HTMLConverter homepage is java.sun.com/products/plugin/converter.html.
Two things to note about using the Plug-In. On Solaris, JavaScript must be enabled for the Plug-In
to work properly. With JavaScript disabled, applets did not load or run when we tested the Plug-In
with Communicator 4.02 and 4.06.

On Win32, the Java Plug-In did not find the identitydb.obj file in the same place that javakey did.
This has to do with different versions of the VM setting different values of the user.home Property.
If you run into trouble, try moving the identitydb.obj file to a different location. Places to try are
mentioned in the section, Creating A Signing Certificate (see page 298).

Distributing Public Keys and Certificates

In order for someone to verify who signed a signed JAR, he or she needs the public key of the
entity who signed the JAR in the first place. Until the public key is distributed to people other than
its owner, no one but the owner can verify that an applet is signed and by whom. Once the signed
applet has been tested and has proven to be functional, it can be placed on a Web site for use by
others.

In order for the applet to escape the sandbox imposed by other people's browsers, users must have
the public key or certificate of the entity that signed the applet. Also, the user must tell the identity
database that he or she trusts the entity that signed the applet. Trusting the entity that signed the
applet allows the applet complete access to the host. Here's how to create a trusted entry:
javakey c signername true

To import the signer's public key contained in keyfile, run:


javakey ik signername keyfile

To import the signer's certificate (which contains the signer's public key) from certfile, run:
javakey ic signername certfile

The identity must be created in the database before trying to import either the public key or
certificate. In order to verify the signature on a signed JAR, you need only the public key of the
signer. Certificates include the public key.

The signer of the applet must make his or her public key (or certificate) available to users of the
applet in some way. It could be linked from a Web site, phoned in, or delivered through email.
Whichever way it is done, the identity must first be extracted from the identity database. To extract
a public key from the database to a file keyfile, use the command:
javakey ek signername keyfile

To extract signername's certificate number 1 to a file certfile, use the command:


javakey ec signername 1 certfile
The information in the keyfile or certfile should be given to those who want to create a policy that
allows applets signed with the identity to leave the sandbox.

CHAPTER SECTION:5

Section 5 -- Differences Between Netscape Object


Signing and JDK 1.1.x javakey
There are five major differences between Netscape and Sun's approach to code signing:

1. Netscape Object Signing only works within Communicator. JDK 1.1 signed applets can
work in any browser, although Netscape Navigator and Microsoft Internet Explorer both
require the installation of the Java Plug-In for the applet to leave the sandbox.
2. Netscape Object Signing requires getting a certificate from a certificate authority such as
VeriSign. JDK 1.1 users can generate their own certificates.
3. Netscape Object Signing requires no modifications to HTML tags. If the Plug-In is needed
for JDK 1.1 (in case you want to use IE or Netscape), the <APPLET> tag must be changed
by HTMLConverter.
4. Netscape Object Signing uses Netscape's own classes to step outside of the sandbox. A
Netscape-specific exception is thrown when permission to leave the sandbox is denied. JDK
1.1 javakey-signed applets do not need to include calls to any other non-java.* classes to
leave the sandbox, and java.lang.SecurityException is thrown when permission is denied.
5. Netscape Object Signing prompts the user when an applet attempts to leave the sandbox,
asking the user for permission to carry out the dangerous act. Actions are grouped, so the
user can allow some actions (file reads) but not others (file writes). JDK 1.1 javakey-signed
applets that are trusted get complete access to the host.

CHAPTER SECTION:6

Section 6 -- Signing Code with Sun's Java 2


The javakey tool from JDK 1.1 has been replaced by two tools in Java 2.

One tool manages keys and certificates in a database. The other is responsible for signing and
verifying JAR files. Both tools require access to a keystore that contains certificate and key
information to operate. The keystore replaces the identitydb.obj from JDK 1.1. New to Java 2 is the
notion of policy, which controls what resources applets are granted access to outside of the sandbox
(see Chapter 3).

The javakey replacement tools are both command-line driven, and neither requires the use of the
awkward directive files required in JDK 1.1.x. Management of keystores, and the generation of
keys and certificates, is carried out by keytool. jarsigner uses certificates to sign JAR files and to
verify the signatures found on signed JAR files.

Getting Started with Keytool

The first step in working with Java 2 is getting the latest beta version from Sun. Members of the
Java Developers Connection (JDC) can download Early Access releases of Java 2 software.
Membership in the JDC is free with registration. Once registered, point your browser
todeveloper.java.sun.com/developer/earlyAccess/jdk12/index.html.

The Win32 version of JDK 1.2beta4 comes with the latest version of the Java Plug-In, which
supports Java 2. During the Install, answer yes when it wants to know if the JRE and the Plug-In
should be installed as well-it will be needed later.

The keytool command operates on a keystore file. The name of the keystore file is .keystore by
default, and it is located in the directory named by the user.home Java System Property. It is
possible to have multiple keystores. Changing the keystore on which the current keytool command
will operate is done through the -keystore <path to keystore> option.

Documentation from the Sun Java Web pages states that Java 2 VMs will run and properly
authenticate JARs signed with JDK 1.1's keytool. It also states that the last beta release of JDK 1.2
does not yet support 1.1-signed JARs. The keytool utility also supposedly allows porting keys and
certificates from 1.1 identitydb.obj files into a Java 2 keystore. According to the documentation, the
command to perform the translation is:
keytool -identittydb -file <path to identitydb.obj file>

Unfortunately, an identitydb.obj file created with JDK 1.1.6 did not successfully import into the
keystore when we tested the keytool from JDK 1.2 beta4-we tried on both Win32 and Unix
platforms. The error message returned from keytool mentions an InvalidClassError and states that a
class used in key management became obsolete, resulting in a serialization error. Until this problem
works itself out in later beta and production versions, the certificates and keys used under 1.1
cannot be used in Java 2. New certificates and keys will need to be generated for use with jarsigner
and keytool.

Generating a public and private key pair and self-signed certificate can be performed from the
command line in one shot without the need to create any directives files. All keys and certificates
stored in the keystore are accessible through an alias. An alias is a name associated with a
certificate entry that keytool uses to uniquely identify each certificate under its control. To generate
a certificate keyed by the alias keyname, run the command:
keytool -alias keyname -genkey

keytool will begin prompting for information. The first prompt is for a keystore password, which
will be needed for all further keytool and jarsigner operations on this keystore. It must be at least
six characters long and is unfortunately echoed to the screen as it is typed. This means that the
keystore password can be leaked to casual observers whenever keytool or jarsigner is used.

Once the password has been entered, keytool prompts for some personal information, such as
name, company name, city, state, and country. All this information is stored in the generated self-
signed certificate, which is saved in the default keystore location. All the personal information is
displayed for verification before keytool generates the keys and certificate. After the certificate and
keys are generated, keytool prompts for another password. Each certificate has its own password,
separate from the keystore password. Entering nothing does not give the key an empty password. It
gives the certificate the same password as the keystore. jarsigner will not prompt for the passwords
of certificates that have the same password as the keystore, so it may appear that a certificate has no
password. However, if the password of the keystore changes, the passwords of the certificates do
not change, so jarsigner will start prompting for not only the password of the keystore, but for the
certificate as well. The command to change the password of a keystore is:
keytool -storepasswd

keytool will prompt for the old password, and the new password twice, all in cleartext. This
command does not affect the passwords of certificates in the keystore, including those that happen
to have the same password as the keystore.

An apparent weakness of the keytool certificate generation system is that a user can accept all the
default values for the personal information prompted for before certificate generation. The default
value for all the questions is "Unknown." So keytool will generate a valid certificate that can be
used to sign JAR files, but is filled with bogus information. No data validation is performed by
keytool, so it is possible to, say, create a certificate for Elvis.

Certificates generated by the system will be valid for just under one year by default. To change the
length of validity for a certificate to n days, add the flag -validity n to the keytool -genkey
command.

To view the fingerprints of certificates in the keystore, use the command:


keytool -list

To view the personal information about the issuer and owner of the certificate, run:
keytool -list -v

Signing a JAR

Once a private key has been generated, jarsigner can be used to mark a JAR file with the public key
of the signer. The command to sign a JAR file called SignMe.jar with the keyname private key
generated previously is:
jarsigner SignMe.jar keyname
jarsigner will prompt for the keystore password and the private key password if different than the
keystore password before signing the JAR file. To monitor the progress of the signing process, run:
jarsigner -verbose SignMe.jar keyname

jarsigner can also be used to verify that a JAR has or has not been signed, and by whom. For a
simple signed/not signed answer for a JAR file Unknown.jar, run:
jarsigner -verify Unknown.jar

To get more information from the verification process, such as the signing status of each file in the
JAR file, the personal information from the certificates used to sign each file in the JAR, and
whether or not the certificate is known in the default keystore, run:
jarsigner -verify -verbose -certs Unknown.jar

After each signed file in the listing will be the personal information encoded in the certificate for
the entity that signed the file. If that certificate is known in the keystore, the name it is known by
will appear in parentheses after the certificate's personal information.

Enter the CA

So far, the only changes from JDK 1.1 are the syntax and the names of the commands. Certificates
can be generated by keytool with any personal information at all. There is nothing to stop anyone
from creating a certificate that claims that it is owned by someone else and signing a JAR with it.
What a Certificate Authority can provide is a level of assurance that a certificate truly represents
the individual that it claims to represent. That is, of course, if you trust that a Certificate Authority
isn't being spoofed, and is properly checking the certificates it vouches for. (Recall, this way
madness lies.)

Certificates generated by keytool can be exported in a form suitable for submission to a Certificate
Authority such as VeriSign. This can be accomplished by running:
keytool -certreq -alias keyname -file requestfile

That command puts a Certificate Signing Request into requestfile for the certificate know by the
keyname alias. However, there is no information as to how to submit this data to a CA for
validation. According to the keytool documentation, the CA will validate the certificate and return
something that must be imported into the keystore. Although we haven't tested it, the command to
import the response from the CA into the keystore is supposed to be:
keytool -import -alias newalias -trustcacerts -file response

That command imports the response from the CA stored in a file called response into the keystore
under the name newalias, which must not already exist in the keystore. The -trustcacerts flag tells
keytool to check the response certificate against the five VeriSign certificates that come shipped
with Java 2 (at least there were five in JDK 1.2beta4).

Turning Over the Keys

Until the certificate used to sign the JAR is made public, no one can grant any permissions to the
enclosed applet. To retrieve a copy of the keyname certificate from the keystore into a file mycert,
use:
keytool -export -alias keyname -file mycert

As usual, keytool will prompt for the appropriate passwords. When the command finishes, the file
mycert can be distributed to users who wish to grant additional privileges to applets signed by that
certificate.

As in JDK 1.1, there is currently limited support for a JAR signed with the JDK tools. Again, Sun
provides support through the Java Plug-In. Plug-In version 1.1.1 does not necessarily support Java
2. Although the Java Plug-In can be configured to use different VMs installed on the local system,
the Plug-In hangs the browser when pointed to a Java 2 VM on Solaris. Documentation for the
Win32 version of the Plug-In mentions running a program off the Start menu to configure the Plug-
In. The installation script does not create a program group for a Plug-In Control Panel as advertised
under Windows NT unless the user performing the installation has permission to create program
groups.

An Early Access version of Plug-In version Java 2 for Solaris is available to members of the Java
Developer's Connection. The latest version of the Plug-In for Win32 ships with JDK 1.2beta4.

As with JDK 1.1, any HTML pages that contain Java 2-signed JAR files must be converted using
the same HTMLConverter used in JDK 1.1. Converting the HTML ensures that the applet will run
in the Plug-In and not in the browser's default VM. See the section on JDK 1.1 JAR signing for
information on where to get the HTMLConverter.

Running a Signed Applet

The first step upon encountering a signed applet is to locate the certificate of the entity that signed
the JAR file and import it into the local keystore. Assuming that the certificate can be located and
placed into a file called acert, run:
keytool -import -alias analias -file acert

An entry in the keystore is created keyed by the name analias for the certificate stored in acert. This
is now a trusted entity. Whereas in JDK 1.1, aliases could either be trusted or untrusted, all aliases
in Java 2 keystores are trusted. However, in JDK 1.1, trusted aliases could do anything they
wanted; aliases in Java 2 cannot do anything unless granted permission. Permissions are granted to
aliases through the use of policy files (seeChapter 3).

Creating a Simple Policy for Signed Applets

Java 2 introduces the notion of policy. Creating, understanding, and managing security policy for
signed mobile code is a difficult and complex problem. Since this discussion is about signing code
and not about constructing policy, an extremely simple example of how to construct policy is
presented. Creating good policy is beyond the scope of this tutorial. The example policy is strong
enough to allow an applet limited file access to the host machine.

Java policy files can be created with the new policytool. This application has a GUI to guide users
though the many twists and turns encountered when creating policy files. It's a very simplistic GUI
with no online help. In its current form as of beta4, it is only useful if one does not know the syntax
of a policy file.

Policy files are plaintext files that follow a format outlined


at java.sun.com/products/jdk/1.2/docs/guide/security/PolicyFiles.html.

The default security policy system first reads a system-level policy file from the lib/security/
subdirectory under the Java installation directory. It then tries to read a .java.policy file from the
current user's user.home directory. In this file, users specify their personal security policy, which
merges with the system security policy. Permissions that can be granted in a Java policy file are
outlined at java.sun.com/products/jdk/1.2/docs/guide/security /permissions.html, as well as
in Chapter 3.

If the policy file is to make reference to a certificate stored in a keystore, a keystore entry must
appear in the policy file. The keystore entry specifies the path relative to the policy file itself and
the name of the keystore file. To keep things simple and use the default keystore file, add the
following line to the .java.policy file in the user.home directory:
keystore ".keystore";

To grant an applet permission to write or create any file in the c:\tmp directory, assuming the applet
comes fromwww.friendly.com/~mybuddy/applets/ and is signed by a certificate known in the
default keystore as friend, add to the .java.policy file:

keystore ".keystore";
grant signedBy "friend",
codeBase "http://www.friendly.com/~mybuddy/applets/" {
permission java.io.FilePermission "c:\\tmp\\*", "write";
};

Note the double backslashes. All Win32 pathnames must use double backslashes to indicate
directories. Unix pathnames use regular singleton forward slashes. CodeBase follows URL syntax.
Sign Only Privileged Code

Applets that request permission to leave the sandbox are usually built for greater purposes than
saving a high-score list on the local drive. Applets that do serious business and hence require access
to the local system are most likely some of the larger applets in existence. It is unlikely that these
applets will be built completely by one developer or one software company. Chances are some of
the components of an applet will be bits of utility code found on the Internet or purchased from a
tool vendor. A smart organization wants to sign only code that it produces; third-party utility code
cannot be safely vouched for.

If all the code is signed, then any code can leave the sandbox based on the policy. However, if
some code in an applet is from a third party, it should not be signed unless the individual signing
the code is willing to vouch that the third-party code won't try to do anything malicious (or
introduce a security hole that others can exploit). To say the least, we don't recommend signing
code you don't completely understand.

Java 2 presents an API for privileged blocks. Privileged blocks are meant to be small sections of
code that have a higher privilege than the code that invoked them. JDK 1.2beta4 introduced a new
API for privileged blocks. Using this API, the only code that needs to be signed is the code that
invokes the AccessController class, and the code that performs the privileged action.

All other code can remain unsigned, preventing it from leaving the sandbox on its own (or tempting
others to attack it). Documentation on the new API can be found
at java.sun.com/products/jdk/1.2/docs/guide/security/doprivileged.html.

There are two things to consider when writing signed code that will be integrated with unsigned
code. First, make the code in the privileged block as small as possible. The less code that is
privileged, the less chance that granting it higher privilege will result in nasty and unwanted side
effects. Second, to prevent mix-and-match attacks, all the code for the applet should live in one
JAR file, even if the third-party libraries that are used by the applet live in their own JAR. (See
Guidelines for Java Developers in Chapter 7, "Java Security Guidelines: Developing and Using
Java More Securely.")

To sign some portions of a JAR file and leave others unsigned takes a number of steps we'll cover
now. First, create a JAR file containing all classes that need to be signed.
jar cvf MyApp.jar Signme1.class Signme2.class

List all the classes that need to be signed in the previous command. Once the JAR containing
classes that need to be signed is created, sign the JAR with jarsigner.
jarsigner MyApp.jar mykey

Now, add the remainder of the classes in the application to MyApp.jar. The Java 2 version of jar
added the v flag, which allows JAR files to be updated with new files.
jar uvf MyApp.jar Other1.class Others.class

List the remaining classes in the application in this step. If parts of the application are already in a
JAR or ZIP file, they will need to be unarchived before being JARed into the new partially signed
JAR file. To verify that all went correctly, use jarsigner to verify the contents.
jarsigner -verify -verbose MyApp.jar

Only the classes that were added before jarsigner was invoked the first time to create the signature
will be marked as signed. All the other classes will be listed, but no certificate or signature will be
associated with their listing. If jarsigner fails to verify the entire JAR, or classes that are supposed
to be signed appear not to be, use the jar command to list the contents of the JAR.
jar tvf MyApp.jar

The first entry in the JAR must be META-INF/MANIFEST.MF. If the manifest file is missing or
not in the first position in the file, the JAR will not verify properly. Following the MANIFEST.MF
file should be a .SF and .DSA (or .RSA) file. If either of those files is missing, then the signature is
missing from the JAR. Remove the JAR file and start over. If the commands listed earlier still
move the META-INF/MANIFEST.MF file out of the first position in the file, it may not be
possible to create a JAR containing signed and unsigned code. (The JAR command with JDK
1.2beta4 did not move the META-INF/MANIFEST.MF file around in the JARs we created.)

CHAPTER SECTION:7

Section 7 -- Differences between JDK 1.1 Code


Signing and Java 2 Code Signing
There are a number of major differences between Sun's approach to code signing in JDK 1.1 and
Java 2:

1. JDK 1.1 trusts code completely or does not trust it at all; Java 2 allows policy to define what
code can and cannot do. This reflects the change from black-and-white trust to shades-of-
gray.
2. JDK 1.1 has one tool, javakey, for all code-signing related functions; Java 2 has keytool for
certificate management and jarsigner for signing and verifying JARs.
3. JDK 1.1 does not support certificates from Certificate Authorities; Java 2 does allow
Certificate Authorities to sign generated certificates, however it is unclear if any CAs
currently offer this service.
CHAPTER SECTION:8

Section 8 -- In Conclusion
Both Netscape and Microsoft have provided browser-specific methods for leaving their sandboxes.
Both rely on external Certificate Authorities to manage identities, but the same certificate used for
Netscape cannot be used for Microsoft. Netscape requires applets to use special classes to take
advantage of code signing. Microsoft also provides a vendor-specific API for certain capabilities.
Both take a similar approach when it comes to prompting the browser's user when certain applets
attempt to leave the sandbox.

Sun has moved from a black-and-white security policy that allowed trusted code to do anything it
wants to a shades-of-gray security policy by which only certain code from certain people can do
certain things, depending upon configuration. However, in Java 2, unsigned code can be granted
free reign of the system as well if the policy is configured as such. Having unsigned code play
outside the sandbox is something that none of the other schemes allow.

Each of the four Java code-signing techniques discussed in this tutorial vary in their complexity
level, have their own special tools for signing and key management, have different levels of support
from VM to VM, and take different approaches to the user's interface to security controls.
Considering that Java is meant to be a portable, mobile code system, the large number of
compatibility issues surrounding code signing is worrisome. Developers want their applets to do
more than the original JDK 1.0.2 sandbox model allowed, but with each vendor providing different
ways for code to leave the sandbox, the goal of "sign once, leave the sandbox anywhere" seems
highly unlikely.
References
Abadi, M., Burrows, M., Lampson, B., and Plotkin, G. (1993) A calculus for access control in
distributed systems. ACM Transactions on Programming Languages and Systems, 15(4):706-734,
September 1993.

Anderson, R. and Kuhn, M. (1996) Tamper resistance-a cautionary note. In The Second USENIX
Workshop on Electronic Commerce Proceedings, pages 1-11. Also available on the Web
at http://www.cl.cam.ac.uk/users/cm213/Publications/tamper.html.

Badger, L. and Kohli, M. (1995) Java: Holds great potential-but also security concerns. Data
Security Letter, 3:12-15. The Data Security Letter (DSL) is published by Trusted Information
Systems (TIS).

Boneh, D., DeMillo, A., and Lipton, R. (1997) On the Importance of checking cryptographic
protocols for faults. In W. Funny (ed) Advances in Cryptology-Eurocrypt'97, Volume 1233
of Lecture Notes in Computer Science, pages 37-51, Springer-Verlag. Also available on the Web
athttp://theory.stanford.edu/~dabo/papers/faults.ps.gz.

CERT (1996a) CA-96.05: Java applet security manager. See


URL http://www.cert.org/advisories/index.html.

CERT (1996b) CA-96.07: Java Security bytecode verifier. See


URL http://www.cert.org/advisories/index.html.

Daconta, M. (1996) Java for C++ Programmers. John Wiley & Sons, New York.

Dean, D., Felten, E., and Wallach D. (1996) Java Security: From Hotjava to Netscape and beyond.
In Proceedings of the 1996 IEEE Symposium on Security and Privacy, Oakland, CA.

Dean, D. (1998) Formal Aspects of Mobile Code Security. Ph.D. dissertation, Department of
Computer Science, Princeton University.

Drossopoulou, S. and Eisenbach, S. (1998) Towards an Operations Semantics and Proof of Type
Soundness for Java. A technical paper to be included in an as yet unnamed book. Available on the
Web on-line at http://outoften.doc.ic.ac.uk/projects/slurp/papers.html.

Erdos, M., Hartman, B., and Mueller, M. (1996) Security Reference Model fo the Java Developer's
Kit 1.0.2. Available from Sun Microsystems and also as a Web document on-line
at http://www.javasoft.com/security/SRM.html.

Fellisen, M. and Friedman, D. (1998) A Little Java, A Few Patterns. MIT Press, Cambridge, MA.

Felten, E., Balfanz, D., Dean, D., and Wallach, D. (1997) Web Spoofing: An Internet con game.
In Proceedings of the 20th National Information Systems Security Conference, Baltimore, MD. An
early version appeared as technical report 540-96 (revised), Department of Computer Science,
Princeton University.

Flanagan, D. (1997) Java in a Nutshell, second edition. O'Reilly & Associates, Sebastopol, CA.

Flanagan, D. (1997) Java Examples in a Nutshell. O'Reilly & Associates, Sebastopol, CA.

Friedman, D., Wand, M., and Haynes, C. (1992) Essentials of Programming Languages. MIT
Press/McGraw-Hill, Cambridge, MA.

Garfinkel, S. And Spafford, G. (1996) Practical Unix & Internet Security, second edition. O'Reilly
& Associates, Sebastopol, CA.

Ghosh, A. (1998) E-Commerce Security: Weak Links, Best Defenses. John Wiley & Sons, New
York.

Gong, L., Mueller, M., Prafullchandra, H., and Schemers, R. (1997) Going Beyond the Sandbox:
An overview of the new security architecture in the Java Development Kit 1.2. In Proceedings of
the USENIX Symposium on Internet Technologies and Systems. Monterey, CA.

Gong, L. and Schemers, R. (1998) Implementing Protection Domains in the Java Development Kit
1.2. In Proceedings of the Internet Society Symposium on Network and Distributed System Security,
San Diego, CA.

Hastings, R. and Joyce, B. (1992) Purify: Fast detection of memory leaks and access errors.
In Proceedings of the Winter USENIX Conference, ACM Press.

Horstmann, C. and Cornell, G. (1997) Core Java Volume I--Fundamentals. SunSoft Press,
Mountain View, CA.

Hughes, L.J. (1995) Actually Useful Internet Security Techniques. New Riders, Indianapolis.

Hughes, M., Shoffner, M. and Winslow, M. (1997) Java Network Programming. Manning.

ISO7816 (1987) International Standards Organization, International Standard ISO 7816-1 through
7816-6 "Identification cards-Integrated circuit(s) cards with contacts". Available through ISO, New
York.

LaDue, M. (1996) Java Security: Whose business is it? Published by Online Business Consultants
and available as a Web document on-line athttp://www.rstcorp.com/hostile-
applets/OBCArticle/Article.html.

Lewis, T. (1996) What's wrong with Java? IEEE Software, 29(6):8. Lewis's letter to the editor was
in response to Java criticism originally printed by him in The NC phenomena: Scenes from your
living room, IEEE Software, 29(6):8-10.

Lewis, T. (1998) Java Holy War '98. IEEE Computer, 31(3):126-128.


Macgregor, R., Durbin, D., Owlett, J. and Yeomans, A. (1998) Java Network Security. Prentice
Hall, Saddle River, NJ.

Martin, D., Rajagopalan, S, and Rubin, A. (1997) Blocking Java Applets at the
Firewall. Proceedings of the 1997 Network and Distributed System Security Symposium. San
Diego, March 1997. Also available on the Web at http://www.cs.bu.edu/techreports/96-026-java-
firewalls.ps.Z.

McGraw, G. and Felten, E. (1996) Java Security: Hostile Applets, Holes, and Antidotes. John
Wiley & Sons, New York. (The first edition of this book.)

McGraw, G. (1998) Testing for security during development: why we should scrap penetrate and
patch. IEEE Aerospace and Electronic Systems, 13(4):13-15, April 1998.

Neumann, P. (1995) Computer Related Risks. Addison-Wesley, Reading, MA.

Oaks, S. (1998) Java Security. O'Reilly & Associates, Sebastopol, CA.

Rubin, A, Geer, D. and Ranum, M. (1997) The Web Security Sourcebook. John Wiley & Sons, New
York

Schneier, B. (1995) Applied Cryptography: Protocols, Alogorithms, and Source Code in C. John
Wiley & Sons, New York. Second edition.

Shimomura, T. and Markoff, J. (1996) Takedown: The Pursuit and Capture of Kevin Mitnick,
America's Most Wanted Computer Outlaw-By the Man Who Did It. Hyperion, New York.

Spafford, E. (1989) The Internet worm program: An analysis. Computer Communications Review,
19(1):17-57.

Stata, R. and Abadi, M. (1998) A type system for Java bytecode subroutines. In Proceedings of the
25th ACM Symposium on Principles of Programming Languages, pages 149-160, January 1998.

Sun Microsystems (1995) The Java language: An Overview. Available from Sun and also as a Web
document on-line athttp://java.sun.com/docs/overviews/java/java-overview-1.html.

Sun Microsystems (1996b) The Java Virtual Machine specification. Web document at
URLhttp://www.javasoft.com/docs/books/vmspec/html/VMSpecTOC.doc.html. Available as a
book by Lindholm and Yellin from Adison-Wesley.

Sun Microsystems (1996c) Low-level security in Java. Web document at


URL http://www.javasoft.com/sfaq/verifier.html/ by Frank Yellin.

Sun Microsystems (1997) Java card 2.0 programming concepts revision 1.0 final. Web document at
URLhttp://www.javasoft.com/products/javacard/index.html.

Venners, B. (1998) Inside the Java Virtual Machine. McGraw-Hill. New York.
Voas, J. and McGraw, G. (1998) Software Fault Injection: Inoculating Programs Against Errors.
John Wiley and Sons. New York. See the Web site at http://www.rstcorp.com/books/sfi/.

Wallach, D., Balfanz, D., Dean, D. and Felten, E. (1997) Extensible Security Architectures for
Java. In Proceedings of the 16th Symposium on Operating Systems Principles (Saint-Malo, France),
October, 1997.

Wallach, D. and Felten, E. (1998) Understanding Java Stack Inspection. In Proceedings of the 1998
IEEE Symposium on Security and Privacy, Oakland, CA.

Wallach, D. (1998) A New Approach to Mobile Code Security. Ph.D. dissertation, Department of
Computer Science, Princeton University.

Young, Boebert, and Kain (1985) Article in an IEEE Tutorial on Computer Network Security.
IEEE Press.

Web sites Referenced in the Text

All of the following links can be found on a page of the companion Web site for this book
at www.securingjava.com.

Chapter 1
Don't Push Me: The Security Implications of Push. developer.com TechFocus article by Gary
McGraw.

Java Developer's Kit (JDK) available free from Javasoft. Also other official Java information.

Javasoft's Frequently Asked Questions: Applet Security

Security Tradeoffs: Java versus ActiveX. Princeton Safe Internet Programming FAQ. Also
see Appendix A.

JavaScript Problems I've Discovered. John LoVerso's JavaScript Security site.

developer.com, an on-line publication for Java developers.

JavaWorld, an on-line publication for Java enthusiasts and developers.

MindQ, an on-line training company specializing in Java.

Yahoo! An excellent starting point for Web surfing. A large Web index.

AltaVista. One of the top search engines on the Web.


Java Security Hotlist. Also see Appendix B.

Princeton's Secure Internet Programming Team. Includes the Java Security FAQ.

The Java Books list. An extensive list of all books published about Java (way too many).

The Java Security Web Site. This book's companion Web site. Includes the Java Security Hotlist.

Chapter 2
The Hostile Applets Home Page, a collection of hostile applets written by Mark LaDue.

Chapter 3
Understanding Java Stack Inspection by Wallach and Felten.

Sun's document explaining the security API change.

Chapter 4
The Hostile Applets Home Page

DigiCrime (disable Java and JavaScript before you surf this site)

The Java Security Hotlist: Hostile Applets and Other Toys

Digicrime's Blue Screen of Death page.

The actual byte code of the bluescreen applet.

Ahpah Software makes the SourceAgain decompiler.

Earthweb's Java applet database. Sun Microsystem's Frequently Asked Questions - Java Security

Princeton's Java Security: Frequently Asked Questions (included as Appendix A)

Princeton's Security Tradeoffs: Java vs. ActiveX (included as Appendix A)

The Java Security Web Site, companion Web site for this book

Chapter 5
An archive of the security-related bugtraq archive

Javasoft's Frequently Asked Questions: Java Security

Princeton Secure Internet Programming Team's Java Security FAQ. Also see Appendix A.
Major Malfunction and Ben Laurie explain the security holes they discovered

Princeton's Secure Internet Programming Team

University of Washington's Kimera Project

Type safety problems discovered in Sun's Verifier by the Kimera Project

Ben Mesander's applet WhereDoYouWantToGoToday

Chapter 6
Princeton's seminal paper, Java Security: From HotJava to Netscape and Beyond

Formalizing the JVM at Computational Logic, Inc.

Javasoft's Security Reference Model for JDK 1.0.2

The Jasmin byte code assembler

Ahpah Software sells the SourceAgain Java Decompiler

Finjan Software, Ltd.

Mark LaDue takes on Finjan

Mark LaDue takes on Finjan again

Digitivity

Security7

WithinReach

Cult of the Dead Cow produces the Back Orifice exploit

esafe

Princeton Secure Internet Programming Team's Java Filter Class Loader

International Computer Security Association

Marcus Ranum discusses firewall certification

Mark LaDue's Hostile Applet Mutation Generator


Chapter 7
The Java Security Web Site, companion site for this book

The Java Security Hotlist

Sun's Java Security FAQ

Martin et al.'s paper Blocking Java Applets at the Firewall

Chapter 8
Gemplus: JavaCard and GemXpresso

Schlumberger: Cyberflex

Javasoft: Java Card Technology, specifications for Card Java can be found here

Boneh, DeMillo, and Lipton's On the Importance of Checking Cryptographic Protocols for Faults

Anderson and Kuhn's Tamper Resistance-A Cautionary Note

Crptography Research, Inc. information on Differential Power Analysis

Das könnte Ihnen auch gefallen