CHAPTER 24
J2EE Security

by Brian Buege and Michael Judd

Java 2 Enterprise Edition (J2EE) is a component- and container-based architecture for building enterprise-level applications. J2EE is not a product; it is a collection of harmoniously working protocols and services. Currently, the J2EE “platform” is popular among enterprise architects and software developers worldwide. The success of J2EE is no accident. Based upon Java 2 Standard Edition (J2SE), J2EE has many features that make it ideal for developing and deploying distributed, network-aware applications in a corporate environment. This chapter will summarize the key aspects of J2EE and explain their implications on the work of systems security professionals.

Java and J2EE Overview

The developers of J2EE rightly decided that building a distributed, network-aware, enterprise application was too difficult for the average enterprise developer to manage, so they strove to remove the complexity of commonly used services from the realm of application development. J2EE is one of the first real attempts at building a true distributed computing platform for application development that spans architectures and provides support for distributed network computing to all developers.

J2EE has been developed with the design goal of allowing developers to create components that, in turn, can plug into vendor-implemented containers. The vendor containers provide certain necessary services, such as transaction support, persistence, threading, and additional security (tuned for a distributed enterprise environment) and save the developer from the complexity of writing these services themselves. The developer simply builds their components, following some simple rules, and deploys them into the J2EE container. Essentially, J2EE adds an enterprise layer on top of the Java Virtual Machine (JVM) that provides a meta operating system in which application components can easily run.

The Java Language

The foundation of J2EE is the Java language, developed by Sun Microsystems. Java has many advantages over traditional application development languages, but one of its most heralded features is its platform independence. There are two main mechanisms that give Java its platform independence:bytecode and a virtual machine. A Java program, like a C program, is compiled using a compiler. However, instead of producing machine code specific to a particular platform, the Java compiler produces a set of instructions called bytecode. This bytecode is then executed by a program called a virtual machine. Essentially, the virtual machine provides the bytecode with a simulated computer in which to run. The advantage of building a system this way is that the only product that needs to be ported to a new architecture is the virtual machine. Once a JVM is implemented for a particular platform, any Java program can run in that JVM without recompilation. It is this feature that makes Java such an attractive platform for enterprise applications; most enterprises consist of largely heterogeneous computing environments and can benefit greatly from the “write once, run anywhere” concept of Java. Developers can work across platforms and applications.

Additionally, Java simplifies many tasks that have historically been difficult for application developers, such as network programming, remote procedure calls, and programming distributed object-based systems. As a matter of fact, Java code libraries can even be loaded, at run time, from a remote system. This gives enterprise application developers exceptional flexibility.

However, one notable side effect of Java’s platform independence and network awareness is that it raises many potential security issues. Since code can be remotely loaded, what if that code is malicious or comes from an untrusted source? Since access to the network is so simple, what if novice application developers expose capabilities that are inherently unsafe? Luckily, Java addresses many of these issues by integrating a very sophisticated security management system at the JVM level. This means that security policies can be enforced at the bytecode instruction level in a Java program. Additionally, the Java security model allows code loaded from an untrusted source to be segregated from trusted code, and for different security policies to be applied to each piece of code based on the location from which it was loaded.

J2EE containers rely upon the JVM specification, so at their foundation, they have the built-in security of the JVM, namely, classloaders, the bytecode verifier, and security managers.

Classloaders Classloaders read the Java class files (composed of Java bytecode) from the file system or from a network URL, and they track the location from which the class was loaded. Code from the local file system is considered trusted code, whereas code downloaded from another source is typically considered untrusted and does not have access to the same system resources.

Bytecode verifier The bytecode verifier examines the Java bytecode before it runs to make sure the code will not attempt any illegal operations. This guarantees that the code that is run will have no adverse effects on the machine on which it runs. Additionally, a Java program, since it is using bytecode and not actual CPU instructions, never accesses a segment of memory directly; pointers to arbitrary memory locations are not allowed in Java. This prevents a lot of the common attacks that are the bane of code written in C.

Security manager Finally, the JVM contains a component called a security manager, which defines the resource containment of the environment. The security manager acts as a watchdog and prevents code from performing actions to which it has not been granted access. In the current Java security model, authorization for running code can be controlled at an extremely fine-grained level, and permissions can be granted based on which user is executing the code, the location the code was loaded from, the digital signature attached to the code libraries, or all three.

Figure 24-1 gives a high-level view of how the JVM manages remote code. The security manager acts as a barrier between untrusted (usually foreign or remote) code and trusted (usually local) JVM code. To interact with the JVM, or to request functionality from the Java runtime API (such as when opening a file), untrusted code must undergo a check by the security manager in force at that time to determine whether or not that code is authorized to perform the task that it is requesting.

image

FIGURE 24-1 A JVM partitioned with a security manager and protection domains

Attacks on the JVM

In the early years of Java (1996 and 1997), Java was somewhat notorious for its susceptibility to a number of attacks involving corrupted or modified bytecode. In their classic book on Java security, Java Security: Hostile Applets, Holes and Antidotes, Gary McGraw and Edward Felten outline several tactics that remotely loaded Java applications or applets containing corrupted or craftily written bytecode could use to defeat the security in place in the common JVMs of the day.

One of the more popular attacks was called a type confusion attack. In this type of attack, an attacker would develop bytecode that could confuse the JVM into thinking that the same location in memory was home to two totally different objects. Essentially, the JVM would be confused as to the type of the object at a particular memory location. Once this condition was established, information could be written directly to memory through one of the objects in question and read using another.

Obviously, if attackers could control one of the interfaces used to read and write, they could use that interface to penetrate and compromise various system-level objects, leading to a complete system compromise. This type of attack was extremely popular when many JVMs were in their early public releases.

Type confusion attacks are possible because of flaws in an underlying JVM, not in the design of the Java security model itself. Therefore, most of the attacks work only on a specific version, release, and patch level of a particular JVM (for example, Netscape Navigator 3.0b5).

Attacks like type confusion are realistic concerns that any security professional or Java user should have. However, in the current world of Java development, JVMs have matured to the point that it is extremely rare to find exploitable flaws in bytecode verification or other JVM-level security tasks. It still happens, but much less frequently.

These risks have been remedied to the point that they deserve to be classified with other system-level security bugs—discover them and install the appropriate patch. In regard to the security of the actual JVM, the best protection is information and diligence. Check the security bulletins from the manufacturer of your JVM (Sun’s bulletins are located at http://Java.sun.com/sfaq/chronology.html). If possible, require your users to use something close to the most current version of the JVM. Making sure that servers are using the most recent build of a JVM will help to give the rest of the J2EE architecture a solid foundation on which to rest.

The J2EE Architecture

Having a solid security foundation like the JVM to rest upon, J2EE security enjoys the benefits of its lineage. However, the fact that the J2EE specification specifically addresses building distributed, networked business applications add much more complexity to its security model.

At the highest level, a J2EE application is composed of servlets, JavaServer Pages (JSP), and Enterprise JavaBeans (EJB) all working together. These three components provide the dispatcher, presentation, and business functionality of a common type of architecture known as Model-View-Controller (MVC). By encapsulating different tasks, such as event handling, user interface, and business logic, into different components, a typical J2EE application gains a “separation of concerns.” This allows different people, with different skills, to contribute to the project without having a deep understanding of the technologies used in the rest of the system. For example, developers that specialize in developing web content can develop that content and interface with other components in the J2EE environment without needing a deep understanding of exactly how business logic components (like EJBs) work.

The purpose of this section will be to review the components that comprise the J2EE environment, show a standard deployment of the containers, and focus on how to secure the connections and network traffic necessary for the J2EE architecture to communicate and operate.

Servlets

A servlet is a Java class that executes in a web container. Most web servers are configured to only serve static HTML content or dynamic content via server plug-ins or CGI extensions. J2EE web containers, on the other hand, are JVMs that provide services that focus on providing dynamic web content using Java technology.

In its simplest form, a Java servlet is invoked by the web container in response to an HTTP request from a client. The servlet can inspect the contents of the HTTP request, execute arbitrary Java code to calculate some information, and then compose an HTTP response that the servlet will pass back to the web container. The web container will then return the response via HTTP to the client.

The primary client of a J2EE application is typically a web browser. Servlets allow a developer to have Java code running within the web container that can be used to dynamically generate the presentation or web interface exposed to the end user. When the servlet API was first released, servlets were seen as a competitor to CGI programming, which was the standard mechanism for generating dynamic web content (primarily HTML) in the late 1990s.

Now, however, the current best-practices document from Sun Microsystems (http://java.sun.com/j2ee/blueprints) recommends that servlet code should not return any HTML, instead delegating that functionality to JavaServer Pages, which are discussed in the next section. Servlets are typically used in a controller role, performing vital application computations and interfacing with other enterprise resources, while the JSPs build the presentation or the HTML interface that will ultimately be seen by the user. This enables the separation of concerns, and allows all of the presentation code to be placed together in the JSPs, which are typically better suited to HTML generation than servlets.

In a typical modern J2EE web architecture, a special servlet known as a controller servlet serves in the role of the main method for a web application; it performs all relevant processing, and it routes requests to the proper location for handling. Because all HTTP request traffic flows through the controller servlet, it is excellently located to parse and validate incoming data, authenticate the users, and dispatch the clients to the appropriate page. Because of this centralized control, the controller servlet can also be used to implement application-level programmatic authentication, authorization, and other relevant security policies if full J2EE security is not needed or desired.

Servlets also have the added benefit of hiding details of the implementation of the components behind them. For example, it might appear from the URL in the browser that the client is continuously going to the same page (the servlet URL), but since the servlet can dynamically forward requests to other components, the system is actually well partitioned, and the requests might be fielded by numerous other components. Using this scheme, the client does not know the names or functionality of the secondary application components and has no way to access them directly. This helps to shield them from attack. The table here summarizes some of the important security-related issues surrounding servlets.

image

JavaServer Pages (JSP)

The JavaServer Pages (JSP) specification was released in 1999. JSPs are an extension to the servlet architecture, and as such, they also run in the web container. JSPs are HTML pages that have special embedded codes in them. They are modeled after Microsoft’s Active Server Pages (ASP) architecture and allow content developers to directly embed Java code into the page, or to have the page call Java code that is written in other classes.

As mentioned in the previous servlet discussion, unlike JSPs, servlets have the HTML presentation pieces included directly in the Java code. This requires that the developer know both Java and HTML coding because, as with a CGI program, the developer is responsible for writing Java code to generate HTML content. The advantage of JSPs over servlets is that an HTML developer can create the initial presentation (the HTML content), and then a few extra lines of Java code can be added to the page in order to add some dynamic presentation pieces at run time. Servlets are Java classes that have a little embedded HTML; JSPs are HTML content that have a little embedded Java code.

The recommendation from Sun is that if a web component is going to generate a significant amount of HTML, it should be a JSP.


NOTE JSPs are parsed and compiled by the web container, which takes the JSP, along with its embedded code, and turns it into a Java servlet. When compiled, the JSP will run as a servlet. So, in reality, there is only one runtime technology used by the web container: the ability to execute servlets. But there are two different ways to create these servlets: manually, or indirectly by composing JSPs.

image

The Role of Servlets and JSP

In a standard size J2EE application, there will be one or more servlets acting as controllers (the front doors) of the application. After running, these servlets will perform the appropriate business computations, and then attach the output data to the HTTP request. They will then forward the request to an appropriate JSP to display the information in HTML format to the user. This process is outlined in Figure 24-2.

Because both servlets and JSP can contain network-aware Java code, they can both be clients to the EJB tier (discussed in the next section), and will typically make requests to EJBs located in another JVM (either locally or remotely).

image

FIGURE 24-2 The web container

There are some tactics that a web component developer can take in order to secure the communication between the JSP and the EJB. First, it may be necessary to encrypt the data so that it is confidential. Second, the web tier may have authenticated the client, so it may be required to pass credential information across to the EJB tier if there are restrictions on the services offered. We will discuss exactly how this communication happens in the section on Internet Inter-ORB Protocol (IIOP) later in the chapter.

Enterprise JavaBeans (EJB)

Enterprise JavaBeans are the last major component in the J2EE architecture. The idea behind EJBs is the encapsulation of business functionality and business data outside of the web container. EJBs run in their own logical container, typically known as an EJB container. By placing these components into their own container, it is possible to move the business logic for an application to a different machine and also to service non-browser clients that do not use the web container or web server at all. That way, both web and non-web applications can share the same business processes and rules by sharing the same code base. Clients of EJBs are typically servlets or JSPs and also heavyweight clients that may be Java applications with normal, application-like GUIs that require shared business functionality or data.

To call an EJB, a client merely makes a series of IIOP requests to the hosting EJB container. For J2EE clients of EJBs, the complexity of composing these IIOP requests, along with all security credential and identity information is hidden from the application programmer and is handled by the J2EE development tools and runtime. However, EJBs can also service non-Java clients as well, via standard IIOP.

There are three major types of EJBs in the latest specifications from Sun:

Session beans These are Java classes that represent business functionality. They are the entry point into the business tier of the application server, and they represent the use cases of business processes. The web container is the primary client of the session beans, but as described previously, a client may be a normal Java application. Metaphorically speaking, session beans provide a library of business functionality that any client, remote or local, can access. They are synchronous by nature, and it is generally assumed that in a J2EE architecture that any synchronous request for business processing will be sent to a session bean.

Entity beans These are Java classes that encapsulate persistent data (data located in a database or another enterprise repository). These beans are typically used by session beans in order to manipulate the data necessary to run their functionality. Entity beans are not typically accessed by the end user directly, since they only represent data and do not contain the complex business rules about how the data should be used.

Message-driven beans (MDBs) MDBs also run in an EJB container and allow an end user to request services without having to wait for a response. These beans act asynchronously and usually are clients of some sort of messaging-oriented middleware (MOM) software, like MQSeries from IBM.

Security becomes interesting with an MDB, since there isn’t a client connected during the time the message is being processed. Special care must be taken when processing the message request to ensure that the message originated from a client that is trusted. Creating a digital signature for the message typically does this. The digital signature is created from the message to be sent and the private key of the sender. If the MDB has access to the public key of the sender, the originator of the message can be verified.

Figure 24-3 depicts the typical roles played by the various types of EJB in an enterprise application.

image

FIGURE 24-3 Typical EJB configuration

image

Containers

A J2EE application requires that JVMs be installed in order to run Java code. The specification calls these JVMs the J2EE containers. Containers provide standard services to the servlets, JSPs, and EJBs, so the developer is not responsible for creating those services for every application. Because of this standardization of services, many security-related issues are handled by the container and not by the individual application developers. For example, a web application developer can specify that their application requires a network transport that maintains message confidentiality. The container is then responsible for ensuring that all communication with that web application takes place over SSL. This is a boon for security administrators, because SSL settings, certificates, and so on, only need to be configured for the web container, and not for every application.

The majority of the J2EE model was designed in this way: standard services, such as authentication, authorization, data transport, and so on, are handled by the container and not by individual applications. This centralizes the management of security policies and configuration, as well as eliminating the potential for insecure coding practices by application developers.

The web container is primarily responsible for running servlets and JSPs—it handles the threading issues for creating and calling web components. The container handles security by allowing the client to be authenticated in a number of different ways, and the developer can also restrict access to web pages or components, so the client must be authorized in order to view them. Finally, the web container also has services that allow the clients to store state within the web tier.

The EJB container is responsible for running the EJBs. It handles services such as persistence for the entity bean and transactions for the business processes in the session beans. Role-based security is also possible within the EJB tier to restrict access of restricted components to only authorized users.

One other important concept upon which J2EE application security is based is that of the deployment descriptor. A deployment descriptor is a mechanism application developers can use to communicate which types of container services their applications require. For J2EE web applications and EJBs, this descriptor takes the form of one or more XML documents attached to the code to be deployed into the J2EE environment. It is in this descriptor that the application developer specifies which application URLs must be protected, what mechanism should be used for authentication, which authorization roles are used by the application, and so on. During deployment, the container reads this descriptor and is responsible for providing those services to the particular application. It is this easy configurability that allows multiple applications, all with different security needs, to be hosted by a single J2EE container. This also allows application developers to specify what type of security their application needs, but leave how that security is implemented up to the container. Therefore, from a security administration standpoint, the single, most important task is configuration of the J2EE containers. If the containers are not configured properly, any security specified by the application developers will be faulty.

The simplest architecture for a J2EE application is a single application server that provides the functionality of a web server, web container, and EJB container. With the addition of a relational database and a browser client, the application becomes an example of the most traditional J2EE application model.

However, for scalability reasons, the web server, web container, and EJB container are typically three separate processes, running on three separate tiers. There is also the possibility of supporting a non-browser-based client in the form of a Java application or of having a client that makes a call to the web tier as a web service. Finally, asynchronous functionality may be added to the application with the addition of message-oriented middleware.

Authentication and Authorization

With all the potential for providing J2EE server resources on different machines with potentially different architectures, auditing the security of a J2EE application can be a daunting process for the security professional. What security mechanisms are available natively in the J2EE architecture that application developers may be overlooking? In this section, we will review common authentication and authorization services provided by J2EE-compliant containers along with a common network “footprint” for a J2EE installation, and some common techniques for securing each segment of the architecture.

J2EE Authentication

For a typical J2EE application, the primary client will be browser-based and will use either the HTTP or HTTPS protocol. There are multiple ways to authenticate the client over these protocols, and one of the benefits of the J2EE architecture is that the web container will actually perform the authentication. This process will occur transparently to the application. As far as the application is concerned, it will receive an HTTP request, and the identity information of the authenticated client will automatically be attached and be available from the container.

Authentication for J2EE web applications is most commonly specified declaratively by adding a security constraint within the web application’s deployment descriptor. Within that is the authorization constraint, which defines the role names authorized to use the particular set of URLs affected by the security constraint. The application developer can also specify a login method for each web application, which tells the container the type of authentication challenge to issue. The three main login methods supported by J2EE containers are HTTP BASIC authentication, form-based authentication, and client certificate–based authentication. Some containers also support HTTP DIGEST authentication.

HTTP BASIC authentication utilizes the authentication mechanisms already built in to the HTTP protocol. When a protected resource is requested, the container will challenge the web client (the browser) with an HTTP 401 response. The browser will subsequently collect the credentials from the user, typically through a login dialog box, encode the user ID and password in Base64, and return them in the next HTTP request to the container as part of the HTTP request header.

J2EE form-based authentication involves the J2EE application returning a page that is an HTML form asking the client for their username and password. When the client submits the form, the container intercepts the request, extracts the user ID and password information from the form, and conducts the authentication with the appropriate identity repository. Then, if the user is authenticated (and authorized) to view the resource they are requesting, the container services the request for the protected resource. The advantage of form-based authentication is that it can be done using HTML pages that blend into the presentation scheme of a web application.

To use form-based authentication for an application, the application developer merely specifies the HTML page to be used to collect the user credentials and an HTML page to display if the login is unsuccessful. The container will then seamlessly intercept all requests for protected resources and use the preceding method to authenticate the user. Again, the application will never see the user credentials. The container will handle all of the details of authentication.

Some containers allow form-based authentication to be done programmatically if the application developer wants to take the responsibility of parsing the form for the username and password and then validating these. It is recommended that the container do these tasks, however, because there are then fewer pieces of code handling sensitive credential information, and the container will most likely handle the actual performance of the authentication in a more standard way.

Form-based authentication typically uses the HTTP POST method and sends the data across the wire unencrypted. In order to make both BASIC authentication and form-based authentication more secure, it is recommended that the deployment descriptor include this line:

image

This will force the exchange of credentials to occur over a secure transport.

The third authentication mechanism is client certificate–based authentication. This mechanism requires that all clients have a signed X.509 certificate. This certificate contains the client’s public key information with a digest signed by a trusted private key (usually from a certificate authority, or CA) as well as the client’s private key, and it allows the server to authenticate based on that information.

For some Java applications, only authenticating clients in the web tier may be enough. In this case, any resources on the EJB tier that need to be protected can be accessed only from restricted pages in the web tier. In fact, this was one of the only options that were available before the J2EE 1.3 specification. Before this, each EJB vendor implemented EJB authentication in a proprietary way. Since the latest specification, though, each EJB tier is required to support Common Secure Interoperability version 2 (CSIv2). This protocol allows a J2EE web container to propagate security credentials across the IIOP protocol to a separate EJB container. Then, the EJB container can authenticate clients rather than having to trust that the authentication took place in the web tier.

J2EE Authorization

The foundation of authorization in J2EE is based on the concept of roles. An application developer specifies a set of application-specific roles and then maps those roles to actions and to resource authorizations within the application deployment descriptor. During deployment, application roles are mapped further to users and groups in the prevailing container’s authentication realm.

For example, an application developer may specify that there are two roles for a particular application: user and administrator. Then, in the security constraint section of the deployment descriptor, the developer may specify that Principals (a principal is a user that has been successfully authenticated) with the role user can access certain URLs, and those in the role of administrator can access other URLs, relative to the application base URL. At no time does the application developer have to know anything about the enterprise environment in which their application will be deployed. They merely invent generic roles and then assign those roles to various resources within the application. The application developer in most cases will never know the actual users or groups ultimately assigned to their roles—they only know that their application authorization scheme requires two types of users (user and administrator in this example).

Protected resources can be URL-based for web components, or individual methods (or functions) for EJB components. When the application is deployed or installed into the J2EE container, the deployer, using the container’s toolset, maps the application role (such as user) to actual users or groups in the enterprise identity schema. Administrators would be configured in a similar way. The container would then manage the authorization for the protected resources based on the results of the container authentication and the role assignments made during deployment.

Essentially, individual clients do not need to be granted permissions. Permissions can be associated with J2EE roles, and then clients can be mapped to roles. The association of users with roles is done at deployment time and is stored in the deployment descriptor. This will work for both the web container and the EJB container.

The fact that the association is done at deployment time is a huge benefit. It means that developers can create components and specify the types of users that will have access. At deployment time, the application can map the name in the component to a role in the system.

Java Authentication and Authorization Service (JAAS)

The future direction of Java enterprise authentication and authorization is called, appropriately, the Java Authentication and Authorization Service (JAAS). JAAS is a relatively new technology, and has been designed to provide Java code with a standard, platform-independent way to both authenticate and authorize clients.

Authentication is done through the use of a JAAS login module. Login modules are pluggable components in the JAAS architecture and are extremely similar in concept, interface, and functionality to Unix Pluggable Authentication Modules (PAMs). The primary goal of a JAAS login module is to determine the identity of the client. During configuration of a particular JVM or application container that supports JAAS, multiple login modules can be specified to authenticate potential clients. These login modules could have been developed by various vendors and could use various mechanisms for authentication, from basic OS-level authentication, to authentication using a Lightweight Directory Access Protocol (LDAP) directory server, to a custom module using biometric hardware or a smart card to authenticate a client. The end result of the authentication is the addition of the appropriate Principal and Credential objects to the prevailing JAAS identity, known as a subject, that represents the identity of the client. A Principal is a way of identifying an authenticated client, such as a username. A Credential is non-identity-based information that is stored with the subject, such as a public key.

Login modules determine the user’s identity in different ways. Sun provides several login modules that are particularly useful: the UnixLoginModule, the NTLoginModule, the Krb5LoginModule, and the JndiLoginModule. Both the UnixLoginModule and the NTLoginModule talk directly to the operating system hosting the JVM to determine whether the user can be identified. The Krb5LoginModule establishes identity with a Kerberos server, and the JndiLoginModule authenticates the user with a naming service compliant with Java Naming and Directory Interface (JNDI)—most likely an LDAP directory. In all of these cases, the login module will gather credential information from the user by using a special object known as a callback handler. Once the module has the user’s credential information, the module will authenticate the user with its default mechanism. The power of the authentication model in JAAS is that login modules can be specified external to application code, and, like Unix PAMs, multiple modules can be chained to provide different levels of authentication for a single user.

Once the client has been authenticated, the JAAS system can also be used for authorization. Interestingly enough, this authorization support is integrated at the JVM level and is done through a construct known as a policy file. The policy file grants permissions to code based on the Principals that have authenticated by the login module as well as the location it was loaded from and the entity that it has been signed by.

In the J2EE environment, the container itself would be configured to use various JAAS login modules to perform its authentication, enabling an arbitrary J2EE container to support any form of authentication that was supported by a JAAS login module. In fact, JAAS login modules are relatively easy to write, and it is increasingly common for enterprises to implement their own login modules that enforce their own authentication policies, perhaps using their own proprietary authentication mechanisms, and then plug them into their J2EE containers. For instance, all Web users could authenticate against the primary enterprise repository, wherever it is.

Protocols

Now that we have covered J2EE from an application and administration perspective, we will look at it from a functional, infrastructure perspective. The purpose of this section is to describe the network protocols that are used within a typical J2EE application. A common server configuration for a typical J2EE installation is shown in Figure 24-4, and you can see that the network footprint of the architecture is considerable indeed. This section will summarize the main protocols used by J2EE containers and provide some suggestions for securing those protocols.

image

FIGURE 24-4 Typical J2EE installation footprint

HTTP

The Hypertext Transfer Protocol (HTTP) was designed in 1991 to serve Hypertext Markup Language (HTML) over the Internet. However, it has evolved over the last decade to support much more than just static HTML pages. In a J2EE application, this protocol is used to communicate between browser clients and the web server or web container. This means that the protocol is used on the public server, and care must be taken when considering the data going across the network. Data is not encrypted in any way, and even worse, the data is passed across the wire in a human-readable format.

HTTP is a request/response-oriented protocol. A client may make a request, typically using the HTTP GET command to port 80, and the server returns a response typically composed of HTML. The request may be as simple as the name of a page to view, or the request may contain the client’s data from a web-based form. In a typical J2EE application, the controller servlet in the web container will have to parse this incoming data to verify its completeness and perhaps its length. The request may then be passed between many different components, which can use database data, business processing, and HTML presentation fragments in order to generate the response dynamically for the client.

An HTTP message is composed of a body and also header information. This header information describes the request or response. For instance, applications may pass parameters to other components in the HTTP request header. HTTP BASIC authentication information is also sent in the request header, and the web container or web server is expected to return status information regarding the request in the response header. For most responses, this message is usually “200 OK,” but the server should return a “401 Unauthorized” response if the client’s authentication credentials are not sufficient for access to a certain page and a “403 Forbidden” response to deny access to a certain page outright.

The way HTTP authentication works for a typical application is that the container returns a “401 Unauthorized” response when asked for a protected resource without proper authentication. The browser recognizes this status code and displays a dialog box asking for the username and password. This information is then encoded and placed in the Authorization field in the header. Because HTTP is a stateless protocol, the server does not remember that there is an outstanding request for a page—the browser simply re-requests the page, but with the addition of the new line in the header. The servlet has access to this data when the request is made, and it can validate the client’s credentials.

Since HTTP is primarily used as a document-transfer mechanism, there are very few security issues that must be addressed. The use of servlet mappings within the web tier creates aliases for components, and it may hide the fact that there is dynamic code being run, making it much harder for a would-be attacker to use the code improperly. Firewalls are almost universal in their support of the HTTP protocol on port 80.

Additionally, there are several HTTP request methods that are usually disabled in web containers and servers, but that can be potentially damaging should they accidentally become reenabled: HTTP PUT and HTTP DELETE. The PUT method allows a client to upload files to the web server or container, and the DELETE method allows a client to arbitrarily delete files from the web server. In a default servlet, these methods are disabled, but application code that enables the methods should be inspected carefully, because potentially damaging side effects could result.

Care must also be taken when using HTML forms. If the form is submitted to the server using the GET method, the form data will be used as part of the location URL. Not only can this data be seen as it passes over the network, but it is displayed in the location bar of the browser and will be captured by any log files tracking page access. Forms should use the POST method to resolve this issue. Additionally, remember that all information sent using HTTP will be sent over the network unprotected. There are no provisions in standard HTTP to verify the identity of the web server or to secure the information in transit.

Because most J2EE web containers are Java based, they are somewhat more resistant to the buffer overflow and scripting attacks that plague other popular web server and scripting environments.

image

HTTPS

The HTTPS protocol was developed by Netscape, and it uses SSL or TLS to encrypt the data passed across the HTTP protocol. In J2EE, the HTTPS protocol can be used for the same purpose as the HTTP protocol, to communicate between the web browser and the web server. The browser will use https:// in the address, and the network connection will typically be established on port 443 instead of port 80.

The encryption when using HTTPS is handled transparently, with the client and the server first communicating the “SSL handshake.” This handshake allows the protocol to pass data between the two tiers that will be used for the encryption process, and the most important piece of data is the server’s X.509 certificate. Certificates are digital files that contain information about the server machine, and most importantly contain the server’s public key. A certificate should be signed by a certificate authority (CA) to provide an additional level of assurance to clients accessing the server.

During the SSL handshake, the server’s certificate is passed to the client browser. If a standard CA has signed the certificate, the browser will use the CA’s public key stored within the browser to verify the digital signature. Once verified, the browser will trust this certificate and approve the connection. If the server’s certificate cannot be verified by the browser, the browser will typically display a message to the client informing them of the fact that the server’s identity cannot be guaranteed, and allowing the user to choose whether to continue or abort the connection attempt.

The client will then create a new symmetric encryption key and pass that data to the server by encrypting the key using the server’s public key. Only the server should have the private key, so the server is the only party able to read the symmetric encryption key. All future data passed across the protocol will be encrypted using this symmetric key.

The benefits to a J2EE application of using this protocol are confidentiality, non-repudiation, and data integrity. In the previous section, HTTP BASIC authentication was used as an example. The username and password were gathered on the client’s machine and passed across to the server in the Authorization header field. This data is encoded into a new format, but it is not encrypted, which means that anyone snooping the network transmission could read the username and password and use that information to attack the J2EE application. With HTTPS, BASIC authentication can still be used, but now the credential data is protected when passed over the network.

Non-repudiation means that it can be proved that someone has been involved in a transaction. For example, in real life, a store could prove that you purchased something by having your signature on the receipt. HTTPS can similarly provide non-repudiation for at least one side of the communication. This is done in two ways. First, the server has a certificate that defines who they are. If a CA has signed this certificate, the data within it should be valid. This is not a good enough guarantee, however, because this certificate is passed across the wire, and an attacker could potentially grab a certificate in transit and then later pretend to be that entity. The second proof is that the browser creates the symmetric key and encrypts that with the server’s public key. Only the server should be able to read this message. Even if an attacker has stolen the certificate, they will not be able to modify it without changing the digital signature of the CA. This means that any message you send will be unreadable by the hacker, because they will not have the private key required to decrypt the text.

Non-repudiation can be done in the opposite direction with J2EE. Besides BASIC authentication, there are other mechanisms to determine who the client is, such as through a client-based certificate. Instead of only the server passing a certificate, both sides could pass certificates. This client certificate authenticates the user to the web server just as the BASIC authentication did, allowing the J2EE application to declare pages or components that only certain users can have access to. This is extremely useful for business to business (B2B) relationships, where both sides need to verify each other. The nice thing about using client-certificate authentication with J2EE is that after the authentication takes place, the identity of the client is automatically available not only to the container, but to the J2EE components. This means that a J2EE component can make decisions based on the identity of the caller, the roles to which they have been assigned, or both.

The final benefit of HTTPS is data integrity. This guarantees that the message that was received is the same as the message that was sent. This is a by-product of all the data being encrypted. If an attacker uses a man in the middle attack and takes data off the wire while adding their own data, the receiver of the modified data could immediately dismiss it because the attacker cannot know the symmetric key being used in the conversation, which means there is no way to create a valid message to send.

All of these benefits are very important to a J2EE application. The application can know who the client is, and based on that can authorize them to certain components. Similarly, the code can modify the persistent data, because it is guaranteed that the client is the one making the requests. And finally, the data is protected from prying eyes.

image

All these benefits come with a price, however. There is a noticeable overhead involved with the SSL handshake, as well as with the encrypting of data as it gets passed. The latency for HTTPS is much higher than for the same data passed using the HTTP protocol. This effect can be mitigated somewhat with the addition of hardware-level HTTP accelerators, but it is still not negligible. For high-volume transactional applications, this overhead can consume significant enough CPU resources to require the addition of more server-side computational resources.

Web Services Protocols

The Simple Object Access Protocol (SOAP) was developed by Microsoft in order to have a simple protocol for invoking remote services using existing web transports, like HTTP. It is the foundation of a bevy of technologies that all fall under the umbrella term web services. The current implementations of SOAP allow Extensible Markup Language (XML) messages to be used within the HTTP protocol, as well as other transports like e-mail, to make remote method calls against J2EE components.

XML allows developers to create documents that not only contain data, but also contain tags that enclose the data and define the meaning of the data. This allows the format to contain self-describing data. This has great potential with Java; Java provides platform-independent code, and XML provides platform-independent data.

SOAP defines what the messages should look like as they pass across the wire. They are independent of the transport protocol that carries them. For the typical implementation-using HTTP, the header information of the HTTP request is not changed; the request itself contains the message body, which is an XML document.

With platform-independent data, a transport-independent protocol, and self-describing messages, the hope for web services is that they will allow all businesses to easily send and receive messages or invoke services of other providers. This is nothing new, as the Common Object Request Broker Architecture (CORBA) and the IIOP protocol allow developers to do this today. However, CORBA is considered such a complicated technology that web services are perceived as the hot, new thing. Standards are still changing in these areas, with Security Assertion Markup Language (SAML) and WS-Security being two of the main players. These two standards strive to provide inter-enterprise authentication, authorization, confidentiality, and integrity models for SOAP messages.

IIOP

The Internet Inter-ORB Protocol (IIOP) is an open networking standard released by the Object Management Group (OMG), and it is the protocol used by CORBA. At a high level, IIOP is a protocol that allows a developer to make remote method calls across the network to run code on another machine.

The CORBA protocol is very useful because it is language independent. Language mappings exist for many programming languages to use CORBA, including C, C++, COBOL, Smalltalk, Ada, LISP, Python, and Java. This means that a new client written in C++ could contact a legacy server written in COBOL. These conversions between languages work by mapping programming language features into interfaces as defined by the Interface Definition Language (IDL) specifications. By defining in the interface how a method can be accessed, any language is free to create its own implementations of the code.

In the J2EE architecture, developers have the advantage of being able to use the IIOP protocol without needing to understand CORBA and without needing to create IDL files to define the interfaces. This is because J2EE uses Remote Method Invocation over IIOP (RMI-IIOP), which is an API from Sun. This API was first added into Java with version 1.3, and it allows developers to transparently use the protocol by only using Java.

In the J2EE environment, IIOP is usually the default communication mechanism for talking to the EJB tier of an application. There are three potential clients that will use the IIOP protocol in a J2EE application: servlets, JSPs, and Java applications.

Servlets In the previous sections, the controller servlet was used to inspect a client request and update the business model for the application based on the request. To do this, a common scheme is for the servlet to invoke one or more methods on the EJB tier via IIOP.

JSPs JSPs that make up the presentation or view component of a system may need access to the EJB tier via IIOP in order to build the HTML presentation for the end user.

Java applications A Java GUI application running as a heavyweight client on a workstation would need access to the data in order to build the presentation.

The HTTP/S protocol discussed in previous sections was defined primarily as a document retrieval system. IIOP allows for remote method (or procedure) calls of code running on other machines. One of the primary differences between these types of protocols is the lookup of services. HTTP/S is hyperlink-based, so the client will request the documents directly. For IIOP, however, a third process is run in addition to the client and server to let the client know what services are available. This process is known as the naming service and it tracks the services that the servers on the system are providing. A J2EE application will use the Java Naming and Directory Interface (JNDI) in order to access these naming service and lookup services. The CORBA naming service typically runs on port 900.

image

Communication Between Components in the Same Container

Sometimes, J2EE components reside in the same container instead of residing on physically different machines or JVMs, as has been the assumption for the majority of this chapter. The 2.0 version of the Enterprise JavaBeans specification defined the use of a new construct called local interfaces. These interfaces allow two J2EE components residing in the same container to communicate without using a network-aware interface, instead communicating internally in the JVM.

Prior to this specification, all calls to all J2EE components were done using a remote (network-aware) interface. That means that a session bean that wanted to use the data present in an entity bean would look up the remote interface from the naming service, even if the two beans resided in the same container (or JVM). This remote interface would allow the session bean to make RMI-IIOP calls to that entity bean. If the session bean and the entity bean were in the same container, the remote request would, in the worst case, have to transit the entire network stack and pass through the loopback interface. Because there is a lot of overhead involved with a remote method call, this caused too much latency considering that the session bean and the entity bean may have been residing in the same J2EE container. The security issue involved with this was that the entity beans (which provide direct access to enterprise data) had to have their remote interfaces defined in the naming service, which meant that any client could look up the reference and make calls to the entity bean directly.

By using local interfaces, the entity beans can be guaranteed that only beans within the container can call them. Developers no longer need to worry about securing these beans against attack.

JRMP

The Java Remote Method Protocol (JRMP) is the native communication mechanism used by Sun’s RMI, which is a Java-based protocol similar to IIOP, but is not language independent. Because of this, JRMP offers some additional features to developers, but it is specific to pure Java installations. Several common application servers are written exclusively (or almost exclusively) in Java, and as such, use JRMP instead of IIOP for internal container-to-container communication (used during clustering, caching, load balancing, and so on).

The default within J2EE applications running inside of containers, however, is not to use the JRMP protocol, preferring instead to use IIOP for component-to-component communication. Using IIOP instead of JRMP may seem very strange, since all of the components in a typical J2EE application are written in Java. However, the one issue with using JRMP for component-to-component communication is that non-browser clients must then always be written in Java. In order to allow any language to be used for the client applications, most application servers default to using the RMI-IIOP protocol that was mentioned previously.

For the purposes of a J2EE application, the discussion of RMI and JRMP is almost identical to the CORBA and IIOP from the previous section. Both are JRMP and IIOP protocols for calling remote methods on another tier. RMI uses a process called the rmiregistry which is a naming service associated with port 1099, while CORBA specifies the COSNaming service, usually on port 900.

Data integrity and confidentiality are not typically a concern for the JRMP protocol in a J2EE application. This is because the protocol is used between the web tier and the EJB tier and is typically used in the context of a J2EE application only for privileged container-to-container communication. If a J2EE application is forcing clients to come through the web tier, the communication between the web tier and the EJB tier may run on a private network and not be accessible to the outside world. However, if applications may be used as clients to the EJB tier, then this application server must exist on a public network in order to be accessed, and securing the RMI-specific ports may be necessary.

Luckily, there is a mechanism built into the RMI protocol to help with data integrity and confidentiality. The RMI communication stack consists of the stub/skeleton layer, the remote reference layer (RRL), and the transport layer. The stubs and skeletons are the proxies the client and server use to communicate. The RRL is used to marshal (to package for transport) the data that is sent back and forth across the wire. (This process—the packaging of complex data structures to be sent across the network—is called marshalling and serialization.) The final layer is the transport layer, and it defines how to make the connections to the other machine. This layer can easily be replaced within the RMI API to allow for a custom socket factory. The current versions of Java even provide new transport layers that encrypt all data using SSL, providing the same advantages that were gained from using the HTTPS protocol.

image

Proprietary Communication Protocols

The last two sections defined two mechanisms for communicating with the EJB tier. It is also possible that the application server has a proprietary protocol that may be used to invoke EJB methods. Each application server can offer a protocol with additional services, perhaps allowing encrypted data or additional ways for passing credential information between the tiers. This information is generally specific to a particular container and thus won’t be addressed here. However, the security professional should be aware that sometimes these protocols will need to be understood and addressed before an installation of a particular container can be considered secure.

JMS

The Java Message Service (JMS) is the API for allowing Java code to interact with messaging systems. Messaging systems supply a loosely coupled, asynchronous communication system. By having components talk to a messaging server, the components do not have to have references to each other, which promotes very loose coupling. The messages are sent to the message server, and the server forwards the messages to the recipients. This frees the sender from having to wait for a response, since it does not directly talk to the recipient. Message servers typically communicate with each other in a vendor-specific, proprietary manner.

In the J2EE 1.3 specification, application servers are required to implement the JMS specifications. The servers will have implementations of both point-to-point queues and publish/subscribe topics. The latest specification has made this functionality easy to use within the EJB tier with the addition of the message-driven beans. Message-driven beans are event-driven components that can be connected to a JMS queue or topic and are triggered when messages from a client arrive.

Because the security mechanisms in many MOM systems are relatively immature and almost always proprietary, J2EE components should use digital signatures and judicious encryption when sending sensitive messages so that the receiver can verify who has sent the message and can know that the information has arrived intact and confidentially.

JDBC

JDBC, from Sun Microsystems, is modeled after ODBC and allows Java code to access any database (that provides a suitable JDBC driver) through a standard Java API.

The Java code that developers write to communicate with relational databases typically uses the JDBC APIs that Sun has created. These APIs define interfaces that must be implemented by the database vendor, and the implementations of these interfaces are known as the database drivers. The JDBC API is defined in such a way that the code should need very few changes even if a new database is chosen for the persistence layer of a particular system. The database driver adapts the requests from the developer into calls specific to the vendor database, usually using the vendor’s proprietary protocol over the network.

In a J2EE application, JDBC is the recommended mechanism for accessing enterprise databases. Typically this will only be done from entity beans running in the EJB tier. There are times, however, when the components in the web tier, or potentially session beans in the EJB tier, may need some information from the database without going through the entity beans.

Clients should not have direct access to the database, so the JDBC protocol should always run across a private network and, if the database server will allow it, between trusted servers. JDBC does support authentication by using a username and password in order to create a connection.

The data traveling across a connection using JDBC is typically not encrypted in any way, since the database needs to be able to understand the messages received. Without code running in the database, there is no way to decrypt this data before it is passed to the actual database. However, many common databases do provide special drivers that allow the client-database communication to use SSL for transport layer security.

Summary

J2EE is an umbrella specification that defines how multiple Java specifications can be used together to build distributed enterprise applications. J2EE containers run servlet components to receive user information and control application flow, JSPs to present information to the end user, and EJBs to execute business processing. The containers provide services of their own. Authentication is one of the important services provided by a typical J2EE container. From a security standpoint, the container is responsible for authenticating the user against the enterprise authentication server, then allowing J2EE applications to specify authorization criteria declaratively or programmatically or both.

Furthermore, application servers have quite a sizeable network footprint, which can make them somewhat unfriendly to a common enterprise security strategy if they are not configured properly. Generally, the best practice is to secure the J2EE environment from the network up: secure the network connectivity between containers, servers, and clients at the transport level, then leverage the strength of an enterprise authentication strategy, and combine this with application-level, declarative, role-based authorization. The J2EE environment presents many opportunities for application developers, but unfortunately, it does this at the expense of network security specialists. However, the good news is that if provided with a solid security foundation, J2EE containers can enforce consistent global security policies across all enterprise applications. They also help remove system and application programmers from the arduous, and often incorrectly performed, task of writing security code, placing it instead in the hands of the container vendors who generally are in a much better position to write such code effectively.

The bottom line is that most common enterprise application security tasks have been addressed by either the J2EE specification or by individual container vendors. This chapter has outlined which tasks are supported and how, at a high level, these tasks combine to help form a comprehensive, secure, enterprise application environment. When actually implementing a security policy in a J2EE environment, this guide should be used as a starting point, but individual details should be gleaned from the appropriate vendor documentation.