The first two parts of this book have described the security issues in running Java programs on a single workstation, usually your PC. But that is only one application area for Java. Java can also be used on a Web server, or any other networked server, in a full-scale client/server approach. In the introduction we stated that security must be holistic, as attackers will concentrate on the weakest links. This applies even more forcefully when many computer systems are connected through a network, as there are more possible points to attack.
This chapter describes a number of different architectural approaches, illustrated with real examples that are in use today. We consider the security implications of these approaches.
Firewalls are often touted as a defense against network attacks. See Firewalls: In and Out of the Net describes how firewalls work, and what the implications are, to both simple users of Web browsers and to Java application designers.
Cryptography is another valuable tool to provide integrity, confidentiality and authentication between distributed systems. We conclude by examining uses of cryptography to provide security to real-world applications.
Perhaps the simplest use of a Java application is the browser add-on applet, to extend the facilities provided by a Web browser. This may be to enhance the user interface, by adding extra interactivity such as context-sensitive help or local search functions. Or it may be to handle additional data types such as compressed astronomical images or packed database records. These examples all depend directly upon the Java security architecture already described, where the security manager and sandbox prevent undesirable access. And because they read data only from the server, if at all, there are no wider security issues.
The next level of complexity is seen in network-aware applets, which perform more network operations than simply reading data. Terminal emulators fall into this category. These applets provide the functions of a "dumb terminal" or VDU (Visual Display Unit), connected via a LAN to a host system, where the applications are run. An example is IBM's Host On Demand, which emulates a 3270 mainframe display session, communicating with a mainframe over TCP/IP (see See Host On Demand ).
When run as an applet, such programs are subject to the restrictions on the Java security manager; in particular, they may only open a network connection back to the system from which they were downloaded. However, terminal emulation programs usually wish to communicate with many different host systems, not just one. If the host is a large mainframe, crucial to business, its owners may be reluctant to install the TCP/IP software, preferring to remain with SNA (System Network Architecture) LANs. And even on other host systems, it might not be desirable to install, configure, run and maintain a Web server just to download the JAVA emulator applet, and this approach would still restrict access to that single host.
One possibility would be to run the Java emulator as a stand-alone application, so relaxing the restrictions on which hosts the emulator may connect to. This is the classic " two-tier client/server" application architecture. The security issues are then very similar to running any other executable program, namely that it is wise to use trusted sources of supply only. Java has some safety and security advantages over other binary programs like .EXE files, and digitally-signed applets can provide a cryptographic guarantee that the code author is who they say they are. It would be possible to create a Java security manager that restricts the functions that the application is allowed to use, but this is not a solution of the non-programming user.
The easiest solution is to run gateway software on the Web server which holds the Java applet. The applet will communicate over TCP/IP with the gateway software, which can then pass through the messages to the ultimate destination. In the case of 3270 terminal emulation, IBM's Communications Server (running on several operating systems) will provide the TCP/IP connection to the Java emulator, and can connect to hosts over both TCP/IP and SNA. This is then a "three-tier client/server" application.
Another approach is to use Web server Common Gateway Interface ( CGI) programs to provide the middle tier. The IBM CICS Internet Gateway takes this approach. To the application server it emulates the functions of a 3270 terminal, but downstream it generates HTML code which is displayed in the Web browser window.
This avoids using Java altogether in the client. It doesn't provide as much flexibility, as the display is restricted to what can be done in HTML. But it may be a simpler solution to the problem. Just because you happen to have a Java-shaped hammer doesn't mean that all solutions must be Java-shaped nails!
The gateway server approach can also be used to provide extended facilities to Java applets. The IBM CICS Gateway for Java is a good example of this; it allows a Java applet to access transaction processing capabilities of CICS servers running on a variety of server platforms. This provides a class library package to access CICS functions. The class library itself does not perform the bulk of the functions; instead, it transmits the request to the gateway server, and returns the server's response to the applet. The gateway server is a small program that receives the requests and calls the real CICS client library, which communicates with the CICS system itself. It would be common to run the CICS transaction processing engine on its own system, separate from the Web server (see See CICS Gateway for Java Example ).
The security analysis for this type of system is more complex. We wish to ensure the security of the gateway system as well as the systems with which it connects, especially if the server is on the public Internet, where any malicious hacker may attempt to access it. Intranet systems should already have some defenses in place to restrict access to company personnel, but security is still of concern, especially where sensitive data is at risk.
The normal approach is to provide a number of barriers which must be overcome before data access is granted. Often the first barrier is the company firewall system (see See Firewalls: In and Out of the Net for more on firewalls). Firewalls can check that requests are coming from, and going to, apparently valid addresses; some firewalls will check the data content of selected protocols, but there are limits to what can be checked. There have been several embarrassingly public demonstrations of Web servers whose content has been replaced by derogatory pages, despite firewalls being in place. Often these hacks have succeeded because valid HTTP URL requests to the Web server allowed software to be run on the server which had an accidental "security hole" in it, such as allowing any data file to be read or written, or even executing arbitrary binary code supplied as part of the URL.
So it is necessary to secure the Web server against as many possible hazards as possible, and also to try to ensure that when (not if!) it is compromised, the attacker still does not have access to critical data.
Hardening Web servers against attack has been the subject of several books, such as Practical Unix and Internet Security by Simson Garfinkel and Gene Spafford, so only a brief checklist will be given here:
These guidelines also apply to any gateway software being run. Try to ensure it does not provide access to more facilities than needed. In particular, don't depend on the client to validate any requests, but assume that a hacker might have constructed a modified client which can generate any possible request. For example, for a 3270 gateway, don't assume that the client will only request connection to a limited set of hosts, but configure the gateway so that those are the only hosts that can be connected to, and that no other host names can be even made visible. For database access and transaction processing, make sure the gateway allows no more than the set of permitted requests.
The classic three-tier architecture pictures can hide other attack routes. The diagram implies that there are separate connections between the client and the Web server/gateway, and the gateway and the end server. But maybe the real network is not configured that way. For simplicity or cost, there might be only a single network interface on the Web server, so that in reality the third tier server is on the same network, and can potentially be accessed directly from the firewall ( See Web Server with One Network Interface ).
Now maybe the firewall is configured correctly, and will prevent direct access to the end server. But will this be true tomorrow, after additional services have been added? For very little extra cost, the networks can be physically separated by providing two network interfaces in the Web server.
(Make sure the cables are well labelled; we have heard of a firewall being bypassed when someone tripped over the cables, and plugged them back the wrong way round!)
Or, a second firewall system can be used, which has the benefit that even if the Web server is compromised, the second firewall still restricts access to the rest of the network. It is more expensive to provide such a " De-militarized zone" (DMZ), though you may require such a configuration in any case, to provide safe Internet connection, in which case there is no extra cost. The cost of a second firewall is likely to be less than the value of the data it protects, so you need to do your own value calculations (this is the configuration shown in See Adding Firewalls to the Mix ).
One additional security barrier to consider using is the type of network itself. You could link the gateway and end server using SNA protocols, or by a small custom-built program communicating over a dedicated serial link ( See Protection Using Mixed Connection Protocols ). These effectively use the network connection as another firewall; if TCP/IP cannot travel over it, many possible hacking techniques are simply not possible. Don't forget, though, that if the Web server is totally compromised, the hacker has all your communications software at their disposal, if they can discover it, so you still should guard the third-tier server.
If you have great concern about what damage an applet may cause on your client, whether by malicious design or by programming accident, you may wish to consider the Network Computer approach. Many types of Network Computers ( NCs) are now available on the market, with varying feature sets. Some are little different from ordinary Personal Computers, though they may have sealed cases to prevent expansion. Some may be intended for domestic use, and connect to a television set and a telephone line, for home Web browsing.
But the type we consider here are the diskless clients, such as the IBM Network Station. This is a small book-sized processor unit, without any local disk, which connects to a local area network (LAN). It has a display, keyboard and mouse. When switched on, it downloads its kernel software from a server on the LAN, and then downloads applications such as a Web browser and terminal emulator. These allow it to run applications on one or more remote servers. The IBM Network Station can also download and run Java programs locally, in fact Java is the only published API for running local programs.
In a secure environment, this has some advantages. There is no local disk storage at the Network Station, so there is little chance of permanent data corruption from malicious or misbehaving software. Although Java programs are not the only things that can run on the NS (it also supports terminal emulation, X-Windows and remote Windows access) there is no capability for integration between the different application types. This means that the Java security restrictions cannot be easily bypassed. All disk storage is held on the servers, allowing a fully managed backup service to be provided. Software updates are performed centrally, reducing administration workload.
For these reasons alone, Network Computers have a great potential in providing universal access to applications and data, with Java as a key technology. The main impetus behind the Network Computer is usually the potential for large cost savings. But in the appropriate application areas, the cost savings may be much less important than the other advantages listed above.
We've described the use of Java at the client in these distributed architectures, but what about using Java elsewhere? This can fulfill the goal of " write once, run anywhere" with a vengeance! It can greatly simplify the work of software developers, especially of distributed architectures. It might be possible to argue that the majority of client systems will be a PC running some flavor of Microsoft Windows, so that you can satisfy most people most of the time by only developing a Windows version of your code. But this is not true for servers; the majority of the world's crucial business data is kept on mainframe and UNIX servers. So if you develop the server side of your distributed application in Java, it will be capable of being run on almost any of these servers, whether they run MVS,VM, OS/390, Windows NT, OS/2, OS/400 or one of the many flavors o f UNIX.
At the other end of the spectrum, the server-side Java might be running in an intelligent peripheral device, such as a printer, modem rack, photocopier or coffee vending machine. At the time of writing, these applications are just in the future, though Web browser interfaces for device configuration are becoming more common. But clearly there are immense opportunities to reduce development costs, providing there is agreement on common standards of Java classes. There are also clear security implications; imagine the effect of re-programming a rival company's vending machine if you managed to break the access codes!
In many ways, Java is an ideal environment for server applications. The multi-threaded environment is ideally suited for supporting simultaneous requests to a server. Even the standard classes are simplified, as many server programs are unlikely to need the java.awt windowing classes as well as several others, which is where most cross-platform problems have arisen to date (especially prior to JDK 1.1).
As an example, the gateway component of the CICS Java gateway could be written in Java, so it could be run on any Web server system without the need for extensive cross-platform porting and testing.
But what is the cost of this portability? In the case of server-side Java, when Java is used as a program development language, the potential risk is reduced execution performance. This is not always a problem; the next section on Servlets shows how Java can sometimes enhance server performance.
Performance is more important for a server than a client, as the server needs to handle many simultaneous users. Just-in-time compilers may help somewhat, but the real solution is to use true Java compilers, at least until processors executing Java bytecode become commonplace. But doesn't this defeat the "write once run anywhere" approach? Not entirely, as vendors can still supply system-independent code, which gets compiled once during the installation process.
True compilers can take two different approaches. The first is to treat Java as just another programming language, and compile Java source into native object code for a given machine. This would imply that software would need to be supplied in source form, which would be less attractive to many developers, although it could be passed through an obfuscating program, to remove meaningful identifiers, etc.
The second approach, which is likely to be more promising, is to compile Java bytecode, rather than source code, into native object code. This allows the compiler to be run on all the wealth of Java bytecode that is available, not just that supplied by server developers. And since Java bytecode is closely related to source code under normal circumstances, some Java true compilers may provide both options and accept source or bytecode input.
Java is not only used to develop stand-alone programs. In our Web-based world, many of the servers run an HTTP Web server. The traditional approach to add customized function to a Web server has been to write Common Gateway Interface (CGI) programs (often termed "cgi-bin" programs after the directory name where they are conventionally stored).
These CGI programs are stand-alone programs which are called by the HTTP server, when it receives requests for specific pages. Rather than return static HTML text, the HTTP server starts the CGI program, and passes it the user's request, together with many details about the server environment. The CGI program must handle the request, and return HTML text to the HTTP server, which in turn returns it to the user:
Starting execution of any program, not just a CGI program, can be a lengthy process. Memory needs to be allocated, the program code needs to be read from disk into memory, references to dynamic libraries need to be linked, standard input and output streams need to be created and connected, and finally the program needs to do the processing required.
In a very simple HTTP Web server, multi-threading may not be implemented, which means that no other HTTP requests could be served until the CGI program returns, possibly after many seconds. Most modern HTTP servers support multi-threading (on appropriate operating systems), so this is less of an issue. But there are still limits to the number of process threads that can be created, as the individual threads still need to wait for the CGI program to complete.
CGI programs are also the target of hackers; many of the successful attacks on Web servers have been through poorly tested CGI programs, which may fail to test the parameters passed to them, or may overflow input buffers when passed overlong data.
Other alternatives to CGI have been implemented, such as NSAPI from Netscape, MSAPI from Microsoft, or ICAPI from IBM. These permit native software routines to be directly called by the Web server, significantly reducing the startup overhead. But the add-on routines still need to be compiled for each platform, and the different programming interfaces may not be fully compatible, restricting the choice of Web server to a particular manufacturer (although ICAPI, for example, has been designed to include the NSAPI calls). Program testing is even more important, to prevent badly written software from corrupting the Web server itself.
Java can be employed to overcome these issues. A " servlet" is a small Java program called by the HTTP server. A JVM is started by the HTTP Web server, and when a request is received it is passed to the servlet object. The servlet must generate the HTML reply, and return it to the HTTP server.
Since the servlet is run from the server, there is no overhead in starting a new process, only that of creating a new Java thread. The built-in safety features of Java will prevent many types of attacks, such as buffer overruns, from taking place. And the Java servlet code is portable to other Web servers and systems. Performance of Java servlets is significantly greater than CGI programs, especially if the CGI programs are written in an interpreted language like Perl.
It is still necessary for servlets to perform some security checking; they need to check their input to ensure they cannot be tricked into returning more information than intended. As they are granted similar privileges to the HTTP server itself, it may be possible for a servlet to read from, or even write to, the HTTP server configuration or log files. Correct programming should prevent this. But deliberate corruption attacks which attempt to overwrite buffers or the program stack should not be possible, due to the built-in safety features of the Java language and the JVM.
CGI uses a transaction model: the client issues a transaction request and then waits until the server returns the results. Distributed object architectures are a more elegant approach. Effectively, the "object space" that an applet or application is working with is extended to include objects on different systems. Client-side Java and server-side Java can be combined to create a full distributed architecture, where functions can be split between the client and server to optimize processing and network loads.
Apart from getting object-oriented purists excited, distributed object architectures have a number of advantages over more conventional transactional systems, including security advantages. For example, you can design systems in which mission-critical objects may be kept safe behind a firewall with access allowed only via method calls from clients. This is far safer than shipping data out of the organization to multiple clients who may simultaneously make changes.
Java JDK 1.1 has provided a tool kit to aid the creation of distributed architectures, the Remote Method Invocation (RMI). This extends the Java object model to the network, by allowing objects in one Java virtual machine to invoke methods seamlessly on objects in another, remote, virtual machine. The remote virtual machine can, in turn, invoke other remote objects.
With RMI, an object, B, residing on one machine (the server) may be manipulated by another object, A, on a remote machine (the client). Object B doesn't really exist on the client, rather an alternative object is used as a kind of "stunt-double." This stub- or proxy-object provides the same interface as the real object B, but under the covers it uses the RMI services to pass method requests over the network to the real object B. Object A therefore doesn't need to know whether object B is local or remote.
If another object, C, needs to be passed between the client and the server - for instance as a parameter for a method - RMI uses a technique called object serialization to "flatten" the object, turning it into a stream of bytes. These are sent to the RMI system on the remote machine, which rebuilds the object C and passes it into the method call. Return values from methods are handled in the same way.
A simple naming service, the RMI Registry, is provided to connect clients and servers together using a URL-style of names, such as rmi://host.port/name. A client asks for the remote objects, and the remote server returns the stub object to the client. Developers use the rmic compiler to generate the matching stub and skeleton classes for a remote object.
This means it becomes possible to write distributed applications, with little need to be aware of exactly where the software will be executed. A RemoteException may be thrown on error conditions, but apart from that, the program need not be aware that portions are executing remotely.
RMI appears to be a straightforward way of creating a distributed application. But there are a number of security issues:
The class loading mechanism also has to be extended to cater for RMI remote classes. When the RMIClassLoader is invoked, it attempts to load classes over the network. A security manager must be defined; otherwise, this would cause an exception. Programmers can write their own security manager, or can use the restrictive RMISecurityManager. This disables all functions except class definition and access. If used, it will also be invoked to subsequently load any local classes. If you require a different (more or less restrictive) security policy, you will need to create your own security manager instead.
If the client and server are connected through one or more firewalls, there are additional issues to be considered. These are covered in See Java Network Connections through the Firewall .
Our conclusions are that you should only use RMI in pure intranet configurations, or for applications where it cannot usefully be attacked. An inter-company chat system may be a reasonable use of RMI, but designing remote objects to represent customer bank accounts would be asking for bankruptcy! Closely coupled internal systems might use RMI, if the appropriate access controls were put in place by network and firewall design. But the lack of authentication and access control in the raw RMI must limit the wider use in secure applications.
If you need to create a distributed secure application, you need to investigate alternatives to RMI. The CORBA (Common Object Request Broker Association) implementations available today provide heavier-weight remote execution methods, and other suppliers can provide alternatives to RMI. Plans are being made to extend JDK 1.2 to include some of these alternative remote execution systems.