Previous Page toc Index Next Page

Chapter 40

Java security

The reaction to Java varies from person to person. Everyone is excited about the functionality that Java provides, but the security considerations polarize people. Either the reaction is “Oh no! you can’t do that” or “What is the big deal? It is just a new language.” In this chapter we hope to explain the “Oh No!” reaction by discussing in depth the security risk involved in Java. Then, you can decide if Java security is important for you.

The answer to the first question, “Isn’t Java just another computer language?” is yes, but it’s how Java is used that opens up the question of security. Java interpreters make it easier than ever before to run new programs on your system; just point and click. (As the warning in Figure 40.1. shows, there are security concerns.)

Figure FIGURE 40.1.

All Java applets in standalone windows come with a warning that shows they are risky.

Once we have discussed the security issues we discuss the Java security mechanisms in detail.

Why Is Security an Issue with Java?

There are no known security vulnerabilities in Java. It is too new, and the developers are working hard to make sure none arise. Experts are skeptical about how secure Java can ever be, however, given how it is planned to be used. We hope to explain these issues so that you can understand them and make your own choice.

Consider the rate at which you currently introduce new programs to your system. If you are purchasing programs, you have to take a trip to the corner software market, and unless you are Mr. Gates, you are going to be able to purchase only a couple of titles. Now think of the time it takes to install the program. If you are surfing the net and bringing back non-Java programs, you may have to compile and then install them. Figuring in work and a social life, that is maybe five to ten programs a week.

Compare that with Java. Once the number of Java applets builds up, you could run new programs on your system as fast as you can find them and click on them. Even figuring in a generous social life, that could easily be hundreds of programs a week.

While the sheer numbers add to some of the risk, as we discuss latter, the biggest risk comes from the fact that you are not going to know who wrote all these programs. Not that you need to know the author of every program you run personally, but the easiest attack on your system is to get you to run a piece of hostile code.

If someone with a mischievous and malevolent bent from, say, Dubuque, Iowa can place a program on a general server, thousands of people may download the program before the undesired side effect of wiping out the hard disk is found. If the perpetrator was unfortunate enough to leave his or her name in the source code, he will be thoroughly chastised. While a reprisal over the Internet is only mildly annoying, it won’t take too many malicious shareware incidents until the victims jump in their cars and drive to Dubuque with pitchforks and burning torches in hand (see Figure 40.2). When the program is anonymous, however, the real-world consequences are null. There is no place for the angry mob to congregate and vent its frustration.

Figure FIGURE 40.2.

If you write malicious programs, beware of mobs of angry computer users seeking vengeance!

Assuming evolution takes care of the stupid perpetrators of mayhem, that leaves the smart perpetrators who post their programs anonymously. They will be able to post their increasingly sophisticated pranks unhindered. Why? Because people love free software. If you tell me a program balances my check book and whitens my teeth for only $299.95 I will scorn it as cheap marketing. However, if the same program is free, I will download a copy ASAP, because, hey, you never know. Effective anonymity from anonymous re-mailers and by people posting from compromised accounts is going to be a part of the Internet for a long time.

Corporations and other organizations have a reputation to maintain, and as a result, they police themselves and remove any malicious programs from their archives. But in the end you are faced with the decision while your mouse is hovering over some unknown applet, “Is this applet going to sauté my system?”

Creating a hostile applet is not easy. A lot of effort has been placed in making Java as safe as possible. Many will argue that creating a hostile applet is not even possible. However, for those that have seen many a “secure” program fall under the sheer pressure of constant probing over time, the possibility of an application being secure when it is first released is extremely low.

Next, instead of getting lost in the nuts and bolts of Java security right away, we are going to discuss some security concepts. Then we show you how Java security maps into these concepts.

Kinds of Attack

The form of attack that is going to be used the most against a system using Java is the “Trojan horse” attack. The old “Cool, they left us this huge wooden horse with wheels. Let’s take it inside” becomes “Cool, here’s a Java applet that whitens teeth. Let’s run it.”

The parallels between the two attacks are astounding. In both cases the attackers found it was too difficult to breach the outer defenses. In the Troy example, it was the city wall. In the computer example it was the connection from your system to the network.

Just what can this applet do once it gets inside? There are three broad classifications of malicious behavior:

We will take a moment to look at each one of these to see just what damage a piece of malicious code running on your system can do.

Denial of Service

One of the easiest attacks to perform and one of the most difficult ones to stop is denial of service. Denial of service, in a nutshell, is that some resource of the system that you were depending on is no longer available to you. This can range from simply crashing the computer (where everything is unavailable) to just eating up all your CPU time and slowing your machine to a crawl.

Say the applet begs you to store some configuration information on your hard disk. (It is true that applets can beg; see the “Security Manager” section.) You think, “I will only let it read and write the one file. What harm can that do?” The applet can now write one very large file, however, filling up your disk and preventing you from saving other work. Once you realize it was the rogue applet, it’s an easy fix, but tracking the problem down is very annoying.

Consider this: once you click on that button you don’t know what that applet is doing. Is the applet really calculating a personal whitening formula based on your tooth enamel density or is it trying to calculate Pi to the last digit? (A classic denial of service attack—one used by countless science fiction heroes against malevolent computers.) Again, all this attack does is annoy you, but there is nothing in Java to prevent it. (Note that you can set the process priority for the applet thread low, but if you really believe the applet is doing useful work, you won’t.)

NOTE
Some computer systems are highly important to organizations. Thus, denial of some service would be catastrophic. If, for example, the malicious program could clog up the computer used for the stock exchange, the costs to investors could be staggering.

Crashing the whole computer from Java is going to be extremely difficult because a great deal of care has gone into creating and testing the Java exception handlers that catch faults in Java applets.

Compromising Information Integrity

This is a more insidious attack (assuming you have valuable data on the system). What is it worth to you to make sure that information remains as accurate as it was when you entered it? Consider, for example, your personal budget. What if a hostile program modified the balance of your checking account so you thought you afford that copy of DOOM with smell-o-vision. Most likely the cost to you is some embarrassment and an overdraft checking penalty.

For an organization, however, the costs could be much larger. The malicious code could modify the financial statements for a company’s prospectus, which could cause lawsuits for misrepresentation. If the malicious code modifies a patient’s record or the software that is used in a pacemaker, the consequences could be death.

In terms of Java, every possible step has been taken to control that a Java applet cannot modify files on the client system.

Disclosure of Information

Another serious attack is disclosure of information. If the information is important to the success of an organization, consider what a competitor can do with that information. Corporate espionage is a real threat, especially from foreign companies where the legal reprisals are much more difficult to enforce.

Assuming the computer is hooked to the Internet, it is as easy as pie for the malicious program to send the information home to the evil-doers. The program could use e-mail or communicate with an Internet server.

In terms of Java, once again every possible precaution has been taken to ensure that an applet cannot read other files on the system. Targeting specific sensitive files is even more problematic, because it’s difficult for a malicious program to know what is sensitive and what isn’t.

The Information Bucket

Now that you have seen some of the attacks, we describe how those attacks are stopped in traditional secure systems. One of the basic premises of computer security is to contain information so that you know where it came from, who has modified it, and where the information can go. Let’s call this concept an “information bucket.” The idea is to put all the related information into the same bucket, and then control who can access that bucket. See Figure 40.3 for some examples. The bucket has also been called the access class, security perimeter, or in DoD systems, the security level.

Figure FIGURE 40.3.

Security starts with clearly labeling and storing information into separate buckets.

For example, most computer systems have the concept of users. Each user gets his or her own little bucket to play in. All the user’s files reside in that bucket, and the user controls access to them.

The system needs to control not only who can access a bucket, but which programs can run in that bucket and what those programs can access. Communication between programs must also be controlled. Programs could signal the information to other programs, which then write the information down in another bucket, as illustrated in Figure 40.4. So which programs can talk to each other must be strictly controlled as well. To summarize, a bucket has a set of programs and a set of files the programs can access. If there is no overlap between buckets, the system is very secure. No one could read or modify data, or consume system resources from another bucket.

Figure FIGURE 40.4.

Communication between programs running in different buckets must be controlled as well.

This would be the equivalent of giving everyone their own computer and not letting them talk to each other. This is obviously an overly restrictive way to solve the security problem. People need to share information. As long as everyone knows what resources are in their bucket and carefully share their information with others, the system is still relatively secure.

The problem comes when the bucket boundaries have not been defined, people are not aware of them, or the bucket boundaries overlap. For example, if two different buckets can read and write the same file, information can flow between the two buckets. In other words, the bucket leaks. Leaky buckets are an indication of potential security problems. Combine leaky/overlapping buckets with a complex system where the number of buckets is very large and it becomes difficult to even discuss how secure the system is.

Once you start opening up the system to allow information to flow between buckets, a new problem raises its head: transitive information flow between buckets. Consider if you give Sally information from your bucket. How do you know that Sally is going to keep your information secret? She may give it to Tom, and because Tom doesn’t know any better he gives it to Polly, your arch enemy (see Figure 40.5).

Figure FIGURE 40.5.

Once information is allowed to move between buckets it is difficult to know were it is going to stop.

As we said earlier, some information needs to be shared between buckets. That is, some leaks are necessary. Special programs can be set up that monitor data being transferred between buckets to ensure only the proper data is leaving the bucket. These programs are trusted to only let the proper data through. It is their job to make sure the entire bucket is not drained.

Writing a program that comes with a guarantee is a difficult thing to do. The easiest approach is to make the trusted program simple so that the program can be analyzed for correctness.

So, you can get a rough measure of the security of a system by considering these three factors:

  1. how many buckets there are
  2. how much overlap there is between buckets
  3. how trusted the programs are protecting those data channels (if information is allowed to move between buckets)

The more overlap between buckets, the more information can flow through the system, and the more analysis is required to ensure the system is secure.

The mechanism that enforces the separation between buckets must also be scrutinized, because it is one of those trusted programs. If information flows between the buckets in ways that was not intended, the system has a covert channel. If any one of the security components that are discussed in the “Security Protection” section has a flaw your system could be compromised. Remember, your Java browser is your friend; it is the only thing protecting you from those potentially hostile applets.

Another consideration for the security of a system is “Are there any exceptions to the bucket policy?” For example, many systems let an administrator play around in any bucket on the system. The problem is not that we don’t trust administrators, but rather that it gives attackers an opening. Now, rather than trying to find a covert channel to peek into another bucket, the attackers try to trick the system into thinking they are the administrator.

Java and the Buckets

Let’s see how Java relates to the information bucket concept. Java has several different kinds of buckets.

Namespace Buckets

The first kind of buckets is the namespace buckets. There are two types of namespace buckets: local and network. The rationale for this split is that all applets originating from the localsystem can be given special privileges because the administrator checked them out before installing them. The network bucket is divided up into smaller buckets: one for each network address. (See Figure 40.6.)

Figure FIGURE 40.6.

The Java namespace is divided into local and network buckets, with network being further subdivided into a names space for each network address.

This means that applets that are from different classes but from the same network site are in the same bucket. This raises a red flag because we have different applets sharing the same bucket. Obviously, the applets are coming from the same network site, so you may think they should be compatible. However, if the applets are from a general server such as a university where a number of people can download applets, or a large service provider where you can rent space for a Web page (such as America Online), the applets could be written by different authors with very different intents. (See Figure 40.7.)

Figure FIGURE 40.7.

Applets loaded from the same network location are in the same nameespace, even though the applets could be written by a diverse set of authors.

An applet may be able to trick another applet at the same site into using its code rather the code of an applet from a different site. Note that the internal class definitions and library calls are always checked first so they can never be replaced by code from an outsider applet.

Method Interface Buckets

The second kind of bucket in Java is the object-oriented interfaces to the Java applets. Each applet can have a public interface that other applets can call. If an applet declares a method as private, it is the only applet that can access the method (see Figure 40.8). The problem with this approach is the buckets are designed by the applications builders. Not all of them are conscientious or trustworthy.

In fact, many applets and code fragments are going to be written by people not concerned with security. People will also integrate applets and code fragments together as they need them. Who knows what the resulting security of these franken-applets will be. All you know for sure is that the applet is contained into one of the namespace buckets.

Figure FIGURE 40.8.

Java methods can be public or be hidden from other applets by being declared private.

Interprocess Communication Bucket

Who can the applet talk to? An applet can almost always talk to one server on the Internet. Thus, there is almost always a channel for the applet to disclose information. Access to other applets or programs is strictly controlled.

Access to the operating system is also tightly controlled. This can be used to prevent the applet from learning your name or other system attributes.

Memory Bucket

When it all comes down to it, the only real buckets are the chunks of memory that the Java system manipulates. Extra precautions have been taken to ensure applets cannot poke around in memory that has not been allocated to them.

This is done by strictly controlling access to the pointer type. Pointers cannot be manipulated by the applet. If an applet could modify a pointer directly, they could point the pointer off into the memory of another applet or even a different namespace bucket. This would be a serious covert channel.

One denial-of-service attack that works against Java is to repeatedly create new objects until all the available memory is gone.

This is why people concerned about security are worried about Java. Although Java separates data into different buckets, some buckets overlap. Also, a great deal of trust must be placed in code that controls pointers.

Improving Java Security Outside of Java

Now that you have an idea of what some of the vulnerabilities are, we look at what can be done outside of Java to protect against them. As pointed out, some buckets overlap, but the overlap is difficult to exploit. Thus, the attacks to be concerned about are covert channels and attacks that use the administrator interface.

The covert channels could only be exploited by malicious code. If you can block malicious code you can prevent the covert channels from being exploited. Detecting malicious code is a challenging job, but that is what the Java Verifier does. We discuss the Java Verifier, with its strengths and weaknesses, in the “Security Protection” section.

Digital Signatures

Another alternative to avoid malicious code is to only run applets that are written without malicious intent. Obviously you can’t quickly and accurately determine the intent of a program. However, if you can be one-hundred percent guaranteed that the applet was written by a person you trust, you know the program was not written with malicious intent. However, you must also guarantee that the code was not modified after your trusted friend finished it.

Crypto-seals or digital signatures allow this by detecting if any changes have been made once your friend signed the applet. Digital signatures even detect if the applet was modified en route to your workstation from the server.

Software Engineering

Simple errors are a common source of security vulnerabilities. You know your trusted friend didn’t put in any intentional security violations, but did he or she put one in accidentally?

Consider this scenario. Bobby’s friend Peter writes an applet that turns scanned images into electronic postcards. Peter makes the applet publicly available on the local school server.

Greg notices that Peter made the getImage() method public. So Greg writes an applet that puts a canceled stamp on the postcard. The idea is that Greg wants people to use his applet at the same time. Greg’s applet also copies the image back to Greg’s server.

Bobby uses the applet to send his mom a postcard of her with a beehive hairdo. Mom retaliates by scanning in that picture of Bobby when she made him be the dresser dummy for his sister’s prom dress: green taffeta, ribbons and all.

Now Greg has just acquired a very interesting, and potentially profitable image. Good software engineering on Peter’s part could have prevented this unfortunate incident. Remember if the software is free, you are getting what you paid for in terms of software engineering.

If you think this example is contrived, think again. It is exactly these kinds of vulnerabilities that were found in the early versions of the HotJava web browser system libraries1. One of the biggest effects on good software engineering is going to be the near random combination of all the applets as people borrow prewritten code fragments and add them to their applications. In the past, people have focused on making their programs work, not on making them secure. There is no reason to believe that will change because of Java. One can hope that solid standard libraries evolve, but it will take time.

Risk Analysis

We have talked about some of the potential vulnerabilities, but what does it mean to you? Are you at risk when using Java? To figure out your risk you must answer several questions:

If you do not have anything to protect on your system, running Java browsers are not a problem because you have nothing to lose. If your system is running software for remote pacemakers, don’t run Java! All other cases fall somewhere between these two extremes and require you to quantify some values.

First, is anyone out to get you? The answer to this is always yes. The propagation of the myriad of computer viruses clearly indicates that there are always going to be people who want to do mischief. As a rule these people have been annoying and random, so they are not really a high-grade threat.

If, however, you are a corporation or large organization, you have information to protect. Competitors are not always going to play by the rules. Now you must figure how much it is going to cost you if your data is compromised and weigh it against the cost of not running Java.

Most people running personal computers out of their homes will probably decide that Java is worth the risk because it saves them time downloading and running software and saves them money (as long as you remove potentially embarrassing data such as things that might involve green taffeta or powder blue leisure suits). Note, if the system is a home business and a personal system, the home business is at risk.

Organizations are faced with much tougher choices.

Security for Organizations

Java is a wonderful tool, but as we have pointed out there are lots of security considerations. These considerations are much more important for organizations.

As organizations become more interconnected, their need to be connected grows. The trend in the past for many organizations has been to introduce the technology without analyzing its effect on the organization. As a result, the ways that information flows through an organization grows. In terms of the bucket model, it’s like just having one big bucket.

Going back to less connected systems with fewer functions is almost impossible for these organizations. Nowhere is this more apparent than in Java. Java brings easy-to-use interfaces that run on a wide variety of platforms. What organization isn’t going to look to Java to solve some of its problems?

The problem with using Java in an organization is that organizations have something to protect. They could lose face, assets, data integrity, or worse. On a personal level the risks are much smaller.

Organizations have to protect themselves not only from outsiders but also from honest mistakes by their own people. As you will see in the next section, many of the final security decisions reside with the end user. In a large organization you can be guaranteed that some user is going to make a bad decision sometime, no matter how much training he or she has, and no matter what clever warning posters the company puts up.

Organizations can have several alternatives for reducing the risk in how they use Java.

Isolating the Organization’s Java Capability

The first alternative is to isolate the corporate network from the Internet (see Figure 40.9). The firewall must stop all requests for Java applets. If someone needs an applet from the Internet, they can request an administrator to go get it. The administrator evaluates the applet, tests it, and then installs it on the internal net. In this approach, the corporate network is treated as one big information bucket. This reduces the risk of getting a malicious applet for the following reasons:

The threat is not eliminated, however, because the malicious code inside an applet could be cleverly hidden. The malicious Java applet may still be able to e-mail home. This solution severely limits Java and only reduces the risk, but it may be suitable for some organizations.

Figure FIGURE 40.9.

One solution for improving Java security for organizations is to not allow users to download applets from the Internet. All applets are downloaded and checked by a Java administrator.

Network Separation

Another approach is completely separating the corporate network from the Internet. Internet workstations would be on a separate net (see Figure 40.10). When Java is needed, employees go to the Internet workstations that have full Java access. This approach is inconvenient, but very secure because there is no sensitive data on the Java net.

Figure FIGURE 40.10.

Another approach to improve security is to keep Java off the internal net entirely. Java is still available, but it is inconvenient.

Advanced Firewall Protection

A hot topic on the firewalls mailing list has been about whether in the future Java applets could be checked at the firewall. The check would be to ensure that the applet had been digitally signed by someone the organization trusts. If the applet was not signed, or if the applet was tampered with, the applet is rejected (see Figure 40.11). The advantage of this approach is that the approach is much easier to administer.

NOTE
For those interested in joining the firewall mailing list write a message to firewalls@greatcircle.com that contains the line subscribe firewalls user@host.
Figure FIGURE 40.11.

In the future, firewalls could be required to ensure that all applets have been digitally signed before being allowed into the internal network.

Users may still sneak applets past the firewall, but the Java verify could be modified to require valid digital signatures as well. Then, even local applets must be approved before they can be executed. Hopefully this functionality will be available soon.

So, Is Java Secure?

Java has no inherent design flaws that make it insecure. However, there are several places where security could be improved. Also, a great deal of trust must be placed in the Java browser to ensure that the applet is properly contained. Even one small implementation error could compromise the entire system.

As a result, there is always the potential that the next applet you bring inside is a Trojan horse that exploits a newly-discovered vulnerability in the Java browser.

Every precaution has been taken to make Java as safe as possible, but in the end everyone must weigh the risks and decide for themselves.

In the next section we discuss the mechanics of Java security and provide some important tips for increasing the security of using Java at your site.

Security Protection in Java

What follows is a generic description of the security measures available to all implementations of a Java Interpreter (that is, any software capable of executing Java applets). The section discusses which components of the Java interpreter are performing the security checks and what those checks are. Discussions of what security measures are available for your favorite Java Interpreter are given later in the chapter.

CAUTION
These security measures are available to Java interpreters, but availability does not guarantee use (much like the availability of mouthwash does not guarantee its use). Even if the security measures are used, there is no guarantee that they were used correctly. Readers are sternly cautioned to choose only reliable Java Interpreters that can be trusted to execute Java applets correctly.

The security measures applied to a class are determined by the origin of the class. The built-in classes that come with the Java interpreter have fewer checks applied to them because they are assumed to be correct and non-malicious. All other classes go through a much more stringent set of checks. Figure 40.12 shows the security checks each class must go through depending on the origin of the class.

Figure FIGURE 40.12.

An overview of security checks. The built-in classes are subjected to a small subset of the available security checks (path 1). Classes loaded from the netwrok (path 2) and classes from the local system (path 3) are subjected to more security checks.

Figure 40.12 also introduces the concept of a security perimeter. For our purposes, we’ll consider the security perimeter to be the line that separates those things you can control from those things you can’t.

First we discuss the path followed by built-in classes that make up the system.

Security Checks for Built-In Classes

For built-in classes, the assumption is that the code is part of the system and therefore must be trusted. Because the code is trusted, many of the checks that could be applied at load- or runtime are not applied, as shown in Figure 40.12. Note that because these classes really are part of the Java interpreter, many of them must be loaded before any checks could be applied anyway.

The built-in classes are those classes stored on your local file system that are located in any of the directories in your CLASSPATH. Your CLASSPATH is an environment variable that contains a colon separated list of directories. Your exact CLASSPATH is user- and system-dependent, but here is an example:

CLASSPATH=”/usr/local/java/classes/:/usr/share/java/classes

One of those directories contains the built-in class file. Again, the exact name of this file is system-dependent, but some of the names used for this file include moz2_0.zip or lib/classes.zip.

CAUTION
Only install vendor-supplied classes as built-in Java classes. Never, ever, store classes of unknown origins in any directory on your CLASSPATH. Putting an unknown class in this directory turns off many of the security checks that are protecting you from malicious code.

The built-in classes are only checked by the following components:

Java Compiler

The Java compiler is a big step toward making sure that the any Java class does not contain any security violations. As shown in Figure 40.13, the Java compiler turns Java source code file into a byte code file. The Java compiler ensures that the safety rules of the Java language are obeyed.

Figure FIGURE 40.13.

The Java compiler turns Java source code into byte code if the code meets the Java Language safety requirements.

Some of the numerous safety mechanisms in the Java compiler are as follows:

File System Loader

Built-in classes are protected to a degree by the file system loader. As shown in Figure 40.14, the file system loader performs one security relevant function. It has the responsibility of placing the built-in classes in a namespace that is separate from the namespace used by other classes. This provides an extra level of protection from unauthorized manipulation by classes.

Figure FIGURE 40.14.

File system loader checks for built-in classes.

Security Manager for Built-In Classes

Built-in classes need not be subjected to any Security Manager checks. Any use of the security features provided by the Security Manager is completely voluntary. Built-in classes are usually supplied by the vendor and require access to system resources without interference from the Security Manager. One reason for this is because the vendor implements the SecurityManager class and puts it in the built-in class file that is loaded with the Java Interpreter. Thus, the Security Manager checks cannot be applied to loading the SecurityManager class, because there is no Security Manager available yet to apply the checks!

Those classes that you write and compile and store in a directory on your CLASSPATH are not forced to call the Security Manager. Thus, you are turning off an important set of security checks for your classes, something we don’t recommended.

For now, it’s sufficient to say that built-in classes are allowed to do the following:

Security Checks for Non-Built-In Classes

All the classes that are not built-in go through a series of security checks as seen in Figure 40.15. All the classes loaded from the network are non-built-in classes. Most of the classes loaded from the local system are non-built-in classes as well.

Figure FIGURE 40.15.

Security checks for non-built-in classes. Network classes do not go through a Java compiler that is under your control.

All Java classes are compiled by an approved, trusted Java compiler, or are they? Because you control the Java compiler, you hopefully are using a good compiler that you can trust to safely compile Java byte code. When you get a Java applet from the net you have no control over the compilation process. The individual producing the applet may have used a bad compiler, or the byte code may have been tampered with after it was produced. Because the Java source and Java compiler are outside of your security perimeter, you can rely on none of the security related mechanisms built into the language or the compiler.

As Figure 40.15 shows, classes under your control follow path three through the compiler you control. However, most of the classes you load follow path two from the Internet (where you do not control the compiler). From a security point of view, it is the bytecode that matters, not the Java source. A benign example is when a buggy Java compiler is used and generates “illegal” pointer arithmetic bytecodes. A malicious example is when an attacker specifically modifies either the compiler or the bytecodes to produce a class that no legitimate Java compiler could ever generate.

These are the practical implications from having the Java source and Java compiler outside of your security perimeter: You can’t prevent either the buggy compiler nor the malicious compiler from producing bytecodes that could cause your computer system to perform unauthorized actions.

Fortunately, the Java interpreter does elaborate checks before loading and executing a class. Each Java class is checked by the following components:

We discuss each component in turn.

Verifier

The Java Verifier is used to check the bytecode to make sure that the safety features of the Java language are followed. As Figure 40.16 shows, the Java Verifier takes byte code, checks it, and only passes the code through if the checks are passed.

Figure FIGURE 40.16.

Java Verifier analyzes the byte codes to ensure that the safety features of the Java language are followed.

The purpose of the Verifier is to allow the Java Interpreter to retain three important properties. These three properties follow:

  1. Applets do not forge pointers, and always use pointers correctly
  2. Applets must use pointers (that is, no alternative access mechanisms exist)
  3. All access restrictions are properly followed

These three properties combine to enable us to make strong security-relevant statements about the system. For example, consider an Applet credit of the type Credit.class that stores your credit card number in one of its private fields, called ccnumber. The security-relevant assertion we make is this: “No other applet can access credit.ccnumber.” One way to show this assertion is true is to examine the ways another applet, attack of the type Attack.class, could attempt to access credit.ccnumber. Some sample attacks are given in Table 40.1.

Table 40.1. These potential attack techniques are stopped by the Java Verifier.

Potential Attack Method Property that Prevents the Attack
attack simply accesses credit.ccnumber #3—All access restrictions are properly followed. Because Credit.class declared ccnumber to be type private, access is denied.
attack creates a class FakeCredit.class which also has a field ccnumber, but makes ccnumber in FakeCredit.class public. Then, attack tries something like this: stolencc =((FakeCredit)credit).ccnumber; #1—Applets cannot forge pointers and must always use pointers correctly. The attempt to recast the pointer credit to a pointer of type FakeCredit is not allowed.
attack.class creates a memory pointer that points directly to the storage that credit.ccnumber uses. #2—Applets must use pointers. Because no alternative mechanisms exist to access an object’s fields, the creation of a “memory pointer” will not succeed in accessing credit.ccnumber.

The first implication is that when your credit card number is stored in a private location, it is protected from all kinds of attacks: unauthorized disclosure, unauthorized modification, and so on.

The second implication is that the Java Interpreter itself is protected as well. The Java system itself (including and especially those parts of the Java system which perform security related functions, e.g. the Security Manager) are protected from malicious modification because of these three properties.

Other aspects of the Java Interpreter help ensure these three properties remain true. The principle protection mechanism concerns Java’s memory-management system. Of primary importance is that bytecodes do not use pointers to a memory location. Instead, the bytecodes use “capabilities” or “handles” to denote access requests. These “capabilities” are resolved to actual memory locations only by the Java Interpreter. Property #2 is a direct consequence of the fact that the Java Interpreter provides instructions of the form “Load this pointer (Capability, for example) into this register.” It does not provide instructions of the form “Load the contents of this memory location into this register.”

Another memory-management protection mechanism results from the fact that the Java Interpreter decides where in physical memory to place the class (or pieces of the class.) Thus, the declaration of a class need not have any bearing on the physical location of the class in memory.

In terms of the credit card example above, our attack.class faces two distinct problems if it is going to “forge a pointer” to credit.ccnumber.

First, using the only starting point available to it (the capability we’ve been referring to as credit), it must determine the memory location of credit.ccnumber. Remember, though, the Java Interpreter has incredible flexibility in determining memory locations. Gone are the days where a simple “memory location of credit plus eight equals the memory location of credit.ccnumber.”

Second, if attack.class does somehow accomplish this feat, and learns that the memory location of credit.ccnumber is “0x2BAD,” it still hasn’t won. The Java Virtual Machine provides no mechanism to perform the function “Load the contents of memory location 0x2BAD.” To succeed, attack.class must construct a request in the form of a Capability such that the same memory location is accessed, but where the access control mechanisms are bypassed. (The first row in Table 40.1 shows what happens when you take the obvious approach and simply refer to credit.ccnumber in a normal manner; the access control mechanism prevents it.) It is becoming difficult to imagine how our attack.class might succeed. It still may be possible for attack.class to access or modify credit.ccnumber; the memory management system just makes it very unlikely that a “forged memory pointer” method is going to succeed.

Details on the Verifier can be found in “Low Level Security in Java.”2 A summary of that paper is presented here. The Verifier performs four separate passes when examining a Java class to be loaded.

The first pass is mainly a syntactic check. It ensures that the class “magic number” is present in the first part of the class file. It also ensures that the class file is neither too short nor too long.

The second pass consists of all the verification that can be accomplished without looking at the class method bytecodes. This pass ensures that every class has a superclass and that the constant pool is constructed and referenced properly.

The third pass consists of checking the bytecodes of each method in the class. A “Data-flow” analysis is performed on each method to ensure various invariants hold true regarding the stack and registers. At the end of this check, it is known that no stack overflows or underflows can occur. Also, each method call is checked to ensure that the correct number and type of arguments are used.

The fourth pass consists of those tests that have been delayed from the third pass due to efficiency reasons. If possible, pass three avoids actually loading the class file. The checks in the fourth pass occur the first time a class is referenced by the Java Interpreter. Further checks occur the first time a field or method within the class is called. These checks ensure that the field or method exists, that all the types match, and that the current execution context has access to the field or method.

Class Loader

After incoming code has been checked by the Verifier, the protections in Java class loader are invoked. The primary function of the class loader is to create and maintain separate namespaces. As shown in Figure 40.17, the class loader begins with a verified Java class, and performs namespace separation on it in preparation for execution.

Figure FIGURE 40.17.

Class loader security checks for built-in classes.

Each network source is given its own namespace and built-in classes are given their own namespace to share.

Additional protections associated with this level are provided by using a safe method to search the namespaces for a class reference. When any class references another class, the file system source namespace is searched first (that is, the built-in classes are searched first). If no match is found, the namespace of the referencing class is searched. If still no match is found, an error is returned. By fixing the order of namespace searches in this manner, it makes it impossible for network source classes to override any filesystem source class. It also makes it impossible for a file system source class to access a network source class by accident.

CAUTION
The surprising implication of namespaces separated by network source creates the strange situation where you should distrust your immediate neighbors more than anyone else! This is because your applet is put into the same namespace as all the other applets loaded from the same server that loaded your applet. This feature supports the applet writer who wants to split the applet into one or more classes: each class is in the same namespace as the others, and thus have access to each other. But this same feature introduces a security vulnerability. If you fail to take proper precautions in your code (for example, you make Credit.ccnumber public instead of private), your applet is vulnerable to attack from other applets. For example, if Attack.class is loaded from the same server as Credit.class, the simple access of credit.ccnumber succeeds. This vulnerability is of special concern to those Java class providers who are using some sort of shared host to serve applets. For example, all aol.com applets, no matter who writes them, are placed in the same namespace. Programmers beware!

Security Manager Security Checks for Classes

The Security Manager is used to provide a flexible access control mechanism for all classes. Any time a class needs to access a system resource, such as a file, the Security Manager is invoked. For each request the storage manager returns if the access is allowed or denied. As shown in Figure 40.18, the Security Manager acts a guardian for the system resources.

Figure FIGURE 40.18.

Security Manager guards system resources from Java classes. Java classes can only access system resources if the Security Manager approves.

Any time a non-built-in class accesses a system resource it must first ask permission from the Security Manager. Classes are prevented from bypassing the Security Manager because none of the system resources are available to ordinary classes. If a class wants to access a resource it must be defined as a method. Only the built-in classes can access system resources directly. However, each built-in class protects its public methods (those that ordinary classes can use) by having those public methods call the Security Manager.

For example, suppose you have a new system resource (say, access to the CD-ROM) that you want to make available to ordinary classes. To do this, you create a new class, create a public method like readCDrom( ), and store the new class in the directory for built-ins. Now any class can call readCDrom( ) to read the CD-ROM. If you want to control access to readCDrom( ),you add a call to the Security Manager in the readCDrom( ) method. Now classes can access the CD-ROM if the Security Manager says it is okay.

All built-in classes which provide access to the system resources are expected to use the Security Manager. The Security Manager provides methods to be called by the built-in classes to check for authorization before performing certain actions. Tables 40.2 and 40.3 provide a list of the protected and public methods, respectively, as well as a short explanation of each the method’s purpose 3.

Table 40.2 lists the fields and methods of interest to programmers wishing to extend or modify the behavior of the Security Manager. For the most part, only programmers creating a Java-capable browser need to be concerned with these fields and methods.

Table 40.2. Protected variables and methods (for use by SecurityManager programmers).

Field or Method Purpose
boolean inCheck Local variable to store state of a “security check in progress.
SecurityManager( ) Constructs a new SecurityManager object(if one doesn’t exist already).
Class[] getClassContext( ) Gets the execution context of this Class.
ClassLoader currentClassLoader( ) The current ClassLoader in the execution context.
int classDepth(String name) Returns the position of the stack frame containing the first occurrence of the named class.
boolean inClass(String name) Returns true if the specified String is in this Class.
boolean inClassLoader( ) Returns a boolean indicating whether or not the current ClassLoader is equal to null.

Table 40.3 lists the methods of interest to all Java users. These SecurityManager methods determine the set of security-relevant checks that the Java Interpreter can perform. Java users can use this table to gain an understanding for which system resources are protected.

Table 40.3. Public variables and methods (for use by SecurityManager programmers and by Java system programmers.

-
Field or method Purpose
boolean getInCheck( ) Returns whether there is a security check in progress.
Object getSecurityContext( ) Returns an implementation-dependent Object that encapsulates enough information about the caller’s current execution context; this context is used to perform some of the security checks later.
checkCreateClassLoader( ) Checks to see if the ClassLoader has been created. It is used to prevent the installation of additional class loaders.
checkAccess(Thread g) Checks to see if the caller can modify the specified thread. The specified thread is allowed to modify the current thread group.
checkAccess(ThreadGroup g) Checks to see if the specified thread group is allowed to modify the thread group.
checkExit(int status) Checks to see if the caller can exit the Virtual Machine (with an exit( ) call).
checkExec(String cmd) Checks to see if the caller can create a new process (that is, run a program).
checkLink(String lib) Checks to see if the caller can cause the specified dynamic library to be loaded and linked (and thus used as native code).
checkRead(FileDescriptor fd) Checks to see if the caller can read the file descriptor.
checkRead(String file) Checks to see if the caller can read the file with the specified system-dependent filename.
checkRead(String file, Object context) Checks to see if the caller’s current execution context and the indicated execution context can both read the file with the specified system-dependent filename.
checkWrite(FileDescriptor fd) Checks to see if the caller can write the file descriptor.
checkWrite(String file) Checks to see if the caller can write the file with the specified system-dependent filename.
checkDelete(String file) Checks to see if the caller can delete the file with the specified system-dependent file name.
checkConnect(String host, int port) Checks to see if the caller can create a socket connected to the specified port on the specified host.
checkConnect(String host, int Âport, Object context) Checks to see if the caller’s current execution context and the indicated execution context can both create a socket connected to the specified port on the specified host.
checkListen(int port) Checks to see if the caller can create a server socket to listen to the specified local port.
checkAccept(String host, int port) Checks to see if the caller can accept a connection request to the specified port on the specified host.
checkPropertiesAccess( ) Checks to see if the caller has access to the system properties.
checkPropertyAccess(String key) Checks to see if the caller has access to the system property named by key.
checkPropertyAccess Â(String key, String def) Checks to see if the caller has access to the system property named by key and def.
boolean checkTopLevelWindow Â(Object window) Checks to see if the caller can create top level windows. A return of false means that the window creation is allowed, but the window will indicate some sort of visual warning. A return of true means the creation is allowed with no special restrictions. (To disallow the creation entirely, this method throws a SecurityException.)
checkPackageAccess(String pkg) Checks to see if the caller can access a package.
checkPackageDefinition(String pkg)Checks to see if the caller can define classes in a package—that is, if the caller can add a new class to the package.
checkSetFactory( ) Check to see if the caller can set a networking-related object factory.

The SecurityManager.class provided in the distribution is not intended to be used directly. Instead, each Java Interpreter is to create its own subclass of the SecurityManager.class in order to implement the desired security policy and security policy controls. Indeed, if some forgetful Java Interpreter tries to use the default Security Manager, it will quickly see that the default answer to every “May I do this?” question is “No.”

By designing the Security Manager subsystem this way, Java Interpreters are provided great flexibility in controlling system resources. Security Policies as simple as “No” and as complex as “Bring up a dialog box to get the user’s permission unless permission has been previously granted, but still always if the request involves deleting a file, but never ask if the execution stack indicates that the request is coming from a built-in class” can both be implemented using the same basic Security Manager mechanism.

The standard pattern for adding Security Manager access checks to system resources is to create an enclosing “guard” function which combines a query to the Security Manager with the actual call to the system resource.

public boolean isFile() {

        SecurityManager security = System.getSecurityManager();

        if (security != null) {

            security.checkRead(path);

        }

        return isFile0();

    }

In this code fragment, the public method <tt>isFile()</tt> first queries the Security Manager (through the call to the <tt>security.checkRead(path)</tt>).

If the Security Manager won’t allow the “read” permission on the file specified by <tt>path</tt>, the call to <tt>security.checkRead(path)</tt> will throw a SecurityException. Because this exception is not caught by the <tt>isFile()</tt> function, the exception will propagate to the caller of <tt>isFile()</tt> immediately. Thus, when an exception is thrown, the low-level function <tt>isFile0()</tt> will not be called.

If the Security Manager will allow the “read” permission on the file specified by <tt>path</tt>, the call to <tt>security.checkRead(path)</tt> will return without error, and the next statement, containing the call to <tt>isFile0()</tt> will be executed.

The previous example shows how the set of public functions in the SecurityManager class can be used in creative manners. Because the SecurityManager class has no direct support for the check <tt>checkIsAFile()</tt>, the check <tt>checkRead()</tt> is used instead.

For the Security Manager to be effective, it must have the cooperation of all the built-in classes that control a system resource. If just one built-in class is implemented that does not follow this “guard” pattern, the consequences could be disastrous. For example, an unguarded write() could allow an applet to overwrite the Java Interpreter’s built-in classes with classes that are all unguarded. When that happens, the security of the system evaporates.

Additionally, the Security Manager relies on the proper functioning of the other security components. These previous levels provide protection against an applet that creates its own version of the Security Manager and protection against unauthorized modification of the built-in Security Manager.

Also, the extent of this cooperation determines the “upper limit” on the kinds of functionality that can be controlled. For example, the built-in classes supporting network functionality (e.g. HTTP, FTP, and so on), currently allow security control to take one of these four forms:

  1. disallow all network accesses
  2. allow network accesses only to the network source from which the class was loaded
  3. disallow network accesses to network sources inside the firewall if the code was loaded from a network source outside the firewall
  4. allow all network accesses

Security in Specific Java Interpreters

Now that we’ve covered the generic mechanisms available to all Java Interpreters, we can now look at some of the available Java Interpreters.

Right now, the generic security concepts in Java are being scrutinized heavily. But it is not the case that each particular Java Interpreter is being subjected to the same level of scrutiny. The danger here lies in assuming that because the concepts are secure, the implementation is secure as well. This need not be the case.

Two things that every implementation of a Java Interpreter depends on are this:

  1. that the Java Virtual Machine is implemented properly. Without this, there is no guarantee that the built-in classes are safe from unauthorized modifications.
  2. that the built-in libraries are implemented properly (including the Security Manager). Without this, there is no guarantee that the built-in classes as stored on disk (among other things) will remain free of corruption.

In summary:

  1. Everyone needs to know Java’s weaknesses in theory
  2. Everyone needs to know their Java Interpreter’s weakness in implementation.

Number 2 is every bit as important as Number 1 if your goal is practical security.

These upcoming sections provide some details on the following Java Interpreters:

AppletViewer

The AppletViewer application is Sun’s utility application capable of rendering Java classes. It is included as part of the Java Development Kit, 1.0 version.

The AppletViewer enables you to run applets without using a completely functioning World Wide Web browser. It is mainly intended as an applet developer’s tool—it has the ability to understand only the <APPLET> tag.

Figure 40.19 shows the AppletViewer’s security control dialog.

Figure FIGURE 40.19.

AppletViewer security control dialog.

The AppletViewer application allows control over the following security-related items:

The AppletViewer has additional security behavior. This behavior is controlled by access control lists. One list controls the set of files that can be read, and another controls the set of files that can be written. By default, both of these lists are empty.

Files (and directories) can be added to either list by creating a file called $HOME/.hotjava/properties or C:\.hotjava\properties. The directives acl.read and acl.write enable the user to specify sets of files that are readable and writeable, respectively.

For example, adding this line

acl.read=/home/tmp/:/home/pub/somefile

to your properties file would let the AppletViewer get a list of files in the directory /home/tmp/ and any of its subdirectories, and would let the AppletViewer read the contents of the file /home/pub/somefile.

Adding this line

acl.write=/home/tmp/:/home/pub/pubdata

would let the AppletViewer create new files in the directory /home/tmp/ and would let the AppletViewer write the contents of the file /home/pub/pubdata.

CAUTION
Once you give applets the ability to create a file or to write a file, there is no way to restrict the size of file the applet writes. It is free to create a file large enough to consume all available disk space, if it so desires.

HotJava

HotJava, version 1.0 alpha3, is Sun’s Java Web browser. Its inclusion in this list, however, is mostly for historic reasons. As of this writing, a new version of HotJava is in development; the version discussed here is not compatible with the new Java API. It is reasonable to assume that this dialog will change in the new HotJava version that is compatible with the new Java API. It is also reasonable to assume that the properties file as used by the AppletViewer application will also be accessed by HotJava.

Figure 40.20 shows HotJava’s security control dialog.

Figure FIGURE 40.20.

HotJava security control dialog.

The HotJava application allows control over the following security-related items:

The HotJava version 1.0 alpha3 release does not permit applets to write files or modify files in any manner.

Netscape Navigator

Netscape Navigator, beginning with version 2.0, is a Java-capable Web browser.

Figure 40.21 shows Navigator’s security control dialog.

Figure FIGURE 40.21.

Netscape Navigator security control dialog.

As the dialog shows, the security preferences in Netscape Navigator concerning Java are straightforward. Java is either enabled (by default) or is disabled.

But, even when Java is enabled, it operates in a very restricted environment. Currently, there is no way for a Netscape Navigator user to remove any of these restrictions:

CAUTION

Applets that happen to reside in your CLASSPATH, however, are not loaded by the applet Class Loader. Instead, they are loaded by the file system loader.

Remember, for security reasons all Java Interpreters check the set of built-in classes before checking elsewhere. This rule applies to class loading as well. By loading a class from the CLASSPATH before loading from another source, all applets are prevented from installing their own private set of what should be built-in classes. For example, if the SecurityManager class had not been loaded when a mischievous applet attempted to load http://www.badguy.com/SecurityManager.class, the Java Interpreter will actually load SecurityManager.class from the local system, not from the external system.

Summary

We have collected the cautions and warnings given throughout the chapter into short practical guides to make your Java experience a safer one:

  1. Be aware that the availability of security measures does not guarantee the use of security measures.
  2. Never, ever, place an unknown .class file in any directory that is in your CLASSPATH.
  3. Never load a .class from a file: URL (unless you know that your Java Interpreter does not treat file: URLs as built-ins).
  4. Be alert that classes being loaded from the same network source can access each other.
  5. Be sure you trust the vendor that implements your Java Interpreter.
  6. Be aware that giving write access to a file means you cannot control the size of the file.

Bringing in programs of unknown origin and running them on your system is a risky business. Java has been designed to take as much risk out of running these unknown programs as possible. Taking the precautions we have outlined in this chapter can further reduce the risks. However, some risks remain. Everyone needs to decide for themselves if the benefits of Java outweigh the risks.

FURTHER JAVA SECURITY INFORMATION ON THE WEB

Joseph A. Bank, “Java Security”, Dec. 8, 1995

URL: http://swissnet.ai.mit.edu/~jbank/javapaper/javapaper.html

James Gosling and Henry McGilton, “The Java Language Environment: A White Paper”, October 1995

URL: ftp://ftp.javasoft.com/docs/whitepaper.ps.tar.Z

Sun Microsystems, “Frequently Asked Questions—Applet Security”, Jan. 9, 1996 version 1.0 Beta 2

URL: http://java.sun.com/sfaq/

Sun Microsystems, “HotJava: The Security Story”, May 19, 1995

URL: http://java.sun.com/1.0alpha3/doc/security/security.html

Sun Microsystems, “The Java Language Specification”, DRAFT—Version 1.0 Beta, October 30, 1995

URL: http://java.sun.com/JDK-beta2/psfiles/javaspec.ps


Previous Page toc Index Next Page