Security becomes an issue as soon as you allow your computing resources to come in contact with the rest of the world. With the recent explosion in the use of networks, preserving the security of data and the resources that carry data has become a primary concern. An open communications port on any computing device almost always carries the potential for abuse: a malicious party may steal or damage sensitive information, network bandwidth, or any other resource associated with your site. Security measures can increase the effort needed for an intruder to gain access to these resources.
In this chapter, we'll look at the Java Security API and how you can use it to make the agents in your distributed application safe from network hostility. We'll briefly discuss the kinds of security concerns you should have as a distributed application developer, and what tools are available in the Java environment for addressing these issues. Some of the issues we'll discuss are common across most applications, so the Java language developers have provided integrated features in the runtime environment that attempt to address them. An example of one of these features is the bytecode verifier, which prevents some kinds of malicious code from running on your machine. Other issues are only important in specific domains and applications, and it's your duty to determine how important these issues are to your particular application, what kinds of measures need to be taken, and how much effort needs to be invested in implementing these measures. For example, consider data theft from communications links. Is your data valuable enough to protect with data encryption, and if so, what level of encryption is appropriate, given the value of the data and the level of effort you can expect from those trying to steal it?
The subject of security in networked environments is worthy of several books' worth of material, and you can find many readings on the subject. In this book, we will only have a superficial discussion of the technical aspects of network security and cryptography, with limited excursions into the details only where it is necessary to support a solid understanding of the topic. From this foundation, we can take an educated look at the security options available to you in the Java Security API, and where you might find them useful.
The next section of this chapter discusses general security issues in networked environments. If you're already familiar with this topic, you can jump right to the later sections, which discuss the design and use of cryptographic security measures through the Java Security API.
Just about everything making up a site on a computer network is a resource with potential value. The most obvious resource you need to worry about is information--the data being sent over the network and the information residing on your host computers. Other resources that could be targets are the applications on your hosts, the CPU resources of your computers, even the bandwidth available on your communications links. A hostile party may want to steal these resources or do damage to them.
Following are some of the things an attacker may do to steal or destroy your resources:
The hostile agent may physically tap into network lines, or set up rogue programs on other hosts to watch for interesting traffic. They may be trying to steal information, or gather information that will help them steal or damage other resources.
This will let them fool you into sending valuable information or giving access to resources they shouldn't have. Our Solver servers could be accessed by intruders acting as legitimate clients, and used to solve their numerical problems; or a hostile party could flood the server with ProblemSets to be solved, rendering the server useless to the legitimate users. Clients of the Solver are also vulnerable, since a hostile party could set up an imposter Solver meant to steal the problem data submitted by clients, or they could purposely generate erroneous results. If the attacker manages to figure out that the clients are trying to solve for stress levels in a finite-element model, for example, then the imposter server could be set up to return results that indicate abnormally high or low stress levels in critical sections of the model. This could cause someone to design a bridge that will collapse, or that's too expensive to build.
Once attackers have located your site, and maybe even stolen some information about what's on the site, they may try to break in to your host and steal or destroy resources directly. For example, attackers may try to crack passwords so that they can log onto your system. A more sophisticated approach is to take advantage of the class-loading and distributed-object features of Java to inject hostile agents directly into a remote host. For example, a client of the RMISolver from Chapter 3, "Distributing Objects" downloads a stub class that it uses for remote method calls to the server. A sophisticated attacker might try to create a "synthetic" remote stub, one whose methods have been modified to steal and transmit valuable information back to the attacker, or to do damage to the remote host from the inside.
This discussion leads us to the following list of general security concerns for the distributed application developer:
Any time you open a network connection to a remote host, either directly, using the java.net.Socket and ServerSocket classes, or indirectly, using higher-level operations like connecting to a remote object, you should be concerned about whose agent you are really communicating with, and on which host machine the agent actually resides.
If you assume that your network transmissions can be tapped into by a hostile party, then you may need to take measures to ensure that only the destination agent is able to interpret and reuse the information you're sending.
Internet-based languages such as Java have brought with them the common practice of allowing agents to "visit" your local environment, either as distributed objects or as applets embedded in an HTML page. So you need a means for screening incoming agents for questionable behavior.
If your other security measures fail and a malicious agent manages to gain access to your local Java runtime environment, you want to minimize or prevent any damage the agent can cause.
Luckily for us, the Java language developers have decided that the last two issues mentioned in the preceding list will be handled inherently by the Java language and runtime. Verification of incoming Java objects is handled by the runtime bytecode verifier. Any classes loaded over the network as applets or distributed objects are checked for correct bytecode syntax and for basic malicious operations. Some of these questionable operations are attempts to manipulate memory addresses directly, or to replace core system classes with network-loaded versions. On top of this, the Java runtime puts restrictions on any loaded code, depending on its source. Applets have minimal access to the local system, for example, and any code has restricted access to classes outside of its own package scope.
If we assume that the Java language developers have done their job in dealing with the last two issues, that leaves the first two for you to worry about as an application developer. Verifying the identity of a remote agent or the source of incoming data requires some kind of certification and authentication process. Keeping communications private on a semiprivate or public communications link involves the use of data encryption. Again, the Java language developers are looking out for you. The Java Security API, introduced in the 1.1 version of the Java Developers' Kit (JDK), provides a framework for integrating these security measures into your applications.
Copyright © 2001 O'Reilly & Associates. All rights reserved.