Mix protocols transparently in Web applications

Implement HTTP and HTTPS in a safe, flexible, and easily maintainable manner

Many Web applications, especially those deployed for e-commerce, necessitate the transmission of sensitive data between the Web server and the client browser. This data could include passwords, credit card numbers, or bank account numbers -- any information users would not want divulged to the general public. To protect sensitive data during transmission, developers at Netscape Communications created Secure Sockets Layer (SSL) and its companion protocol, HTTP over Secure Sockets Layer (HTTPS). HTTPS employs SSL to protect data by encrypting it at the source, be it the server or the client, and decrypting it at the destination, thus preventing anyone monitoring Internet data transmissions from easily capturing this data. The client and server exchange public keys to enable encryption and decryption.

The encryption/decryption process comes at a performance price, however. Data throughput for a Web server transmitting via HTTPS is often as little as one-tenth that of data transmission via HTTP. For this reason, you shouldn't deploy an entire Web application under SSL. For fastest performance, deploy a Web application under HTTP and employ HTTPS only for those pages and processes that transmit sensitive data. In this article, I propose and develop a solution for implementing this protocol mixture.

Current SSL implementations: Static links

Perhaps the most prevalent approach for integrating SSL into a Web application is to specify the entire URL, including the HTTPS protocol, in those hyperlinks that lead to Webpages or servlets requiring HTTPS. This leads to HTML code like the following snippet from a page not requiring SSL:

<a href="../nonssl/insecurePage.jsp">Non-SSL link</a>
<a href="https://www.somedomain.com/ssl/securePage.jsp">SSL Link</a>

Similarly, pages requiring HTTPS should specify the HTTP protocol in hyperlinks that lead to pages or servlets that do not require the extra data protection. The following HTML would come from a page requiring SSL:

<a href="http://www.somedomain.com/nonssl/insecurePage.jsp">Non-SSL link</a>
<a href="../ssl/securePage.jsp">SSL Link</a>

Advantages

One advantage to this method: you can easily implement it during development. You need no mechanism beyond what basic HTML provides.

Disadvantages

As is often the case, what proves easy to implement during development turns into a maintenance problem in production. Changing the protocol for any particular Webpage or servlet requires that you find and edit all links to that page or servlet to specify the new protocol. For portability reasons, you should specify hyperlinks in a fashion relative to a common directory or root context. Forcing the entire URL specification in hyperlinks creates a maintenance problem when the application moves from development to deployment, or any time the domain name or root context changes.

The biggest problem with the static link implementation is that nothing prevents a user from specifying the wrong protocol by manually entering a URL into the browser. The penalty for manually specifying HTTPS for a page or servlet not requiring HTTPS: reduced performance. Far worse is the penalty for manually specifying HTTP for nonsecure access of a page that does require HTTPS: sensitive data exposure.

Current SSL implementations: Restrict access

To prevent nonsecure access of sensitive data, the Java Servlet Specification 2.2 (and 2.3) defines the user-data-constraint element of the deployment descriptor for Web applications, better known as the web.xml file. As a child of the security-constraint element, user-data-constraint contains the transport-guarantee element. This element must specify one of three protection types for communication between client and server: NONE, INTEGRAL, or CONFIDENTIAL. While a NONE designation means that the Web resource being specified requires no transport guarantees, an INTEGRAL designation indicates that the Web resource must transmit between the client and server in a way that prevents changes to the resource's data while in transit. CONFIDENTIAL means that the Web resource's data must travel in a way such that no one can observe it while in transit. Most containers -- including BEA's WebLogic Server 6.1, which we'll use in this discussion -- treat the INTEGRAL or CONFIDENTIAL designations as a requirement for SSL usage. When a Web resource is specified as INTEGRAL or CONFIDENTIAL in the web.xml file as shown below, the user cannot access that resource over HTTP:

<security-constraint>
    <web-resource-collection>
       <url-pattern>/SomeSslServlet</url-pattern>
    </web-resource-collection>
    <user-data-constraint>
       <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>

If a user attempts to access such a resource over HTTP, perhaps by manually entering the URL into her browser, a page pops up informing her that she needs SSL to access the requested resource. When WebLogic is the container, this message reads, "Need SSL connection to access this resource."

Advantages

This approach combined with the static links method continues to provide the deployment advantages described for the static links method. The use of user-data-constraint in the deployment descriptor adds little complexity to an existing web.xml file. Using the deployment description in this way eliminates sensitive data exposure, which was the greatest disadvantage of using static links alone.

Disadvantages

The static link approach's other disadvantages remain. Link maintenance within pages will continue to prove troublesome for the same reasons outlined earlier. A new problem, however, might surface, where a user confronted with the given error message or even a more descriptive page might not understand the need to use HTTPS for accessing a secured Web resource. The message might intimidate or frustrate her; rather than re-enter the URL with the appropriate protocol, she might simply leave the Website.

Better solution needed

The ideal solution: an approach that automatically uses the correct protocol when accessing Web resources. This would both prevent access via an inappropriate protocol and shield users from re-entering URLs. The ideal solution should also prove easy for developers to implement and maintain.

Java Web application resource flow mechanisms

To develop our desired solution, we need to devise a process for routing users to the appropriate protocol for each Web resource. J2EE (Java 2 Platform, Enterprise Edition) provides two mechanisms that send users to another URL.

The first of these mechanisms is the forward() method found in the RequestDispatcher interface. Web applications that follow the MVC (Model-View-Controller) architecture often use this method to forward a request from a servlet to a JSP (JavaServer Page). A typical forward() instance within a servlet looks like this:

aRequest.getRequestDispatcher( "/somePage.jsp" ).forward(aRequest, aResponse);

A typical instance within a JSP, like this:

application.getRequestDispatcher( "/somePage.jsp").forward(aRequest, aResponse);

However, this limited mechanism can forward only to either another resource with the same root context or another context with the same document root, which includes the request protocol. This limitation prevents us from using the mechanism to forward a request with another protocol.

The second mechanism, the sendRedirect() method in the HttpServletResponse interface, provides the power to route to any URL with any protocol, as shown here:

aResponse.sendRedirect("http://some.otherdomain.com/aPage.jsp");

The only caveat here is that a response can only issue a redirect before it has committed. If a response attempts a redirect after committing, the sendRedirect() method will throw an IllegalStateException. With this limitation in mind, we select the sendRedirect() mechanism for use in our SSL implementation solution because of its greater flexibility in URL specification.

Solution proposal

In addition to the redirect mechanism, we will use two other methods from the Java Servlet API: We use the getScheme() method on the ServletRequest interface to determine whether a Web resource was called using the HTTP or HTTPS protocol. The getRequestUrl() method on the HttpUtils class tells us what URL requested the Web resource. (Note: This method has moved to the HttpServletRequest interface in the Servlet 2.3 specification.)

Basic solution algorithm

The fundamental algorithm for our solution:

  • Determines the protocol used to request our Web resource
  • If that protocol matches the protocol we want for this resource, it does nothing
  • If that protocol doesn't match the protocol we want, it redirects to the same URL using the correct protocol

As an example, if a user issues a request to SomeSslServlet using the URL http://www.somedomain.com/SomeSslServlet, our algorithm redirects the request to the URL https://www.somedomain.com/SomeSslServlet.

Develop the solution

The code corresponding to our algorithm, in its simplest form, appears below:

   String desiredScheme = "https" ; // or "http"
   String usingScheme = aRequest.getScheme();
   if ( !desiredScheme.equals(usingScheme) ) {
              StringBuffer url = HttpUtils.getRequestURL(aRequest);
              url.replace(0, usingScheme.length(), desiredScheme );
              aResponse.sendRedirect(
                    aResponse.encodeRedirectURL(url.toString()));
              return;
   }

The return statement after the redirect is necessary to terminate the thread executing the Web resource containing the logic. The desired scheme's specification could be hardcoded as shown here or read from an external source to allow the desired protocol's specification at deployment time for each Web resource. An external source could be a properties file, a database table, or the web.xml deployment descriptor file, as you will see next.

Use standard ports

This above implementation assumes the use of the standard HTTP and HTTPS ports of 80 and 443 respectively. When using these ports, we don't need to specify the port number in the URL request. Therefore, when using the default ports, we don't need to specify a different port number when we redirect from one protocol to the other.

Use nonstandard ports

If we want a more general solution, we must allow for nonstandard port numbers in our Web application. Rather than hardcode the port numbers, we rely on the Java Servlet Specification's convenient mechanism for defining such properties at deployment time through the deployment descriptor.

This mechanism, the context-param element in the web.xml file, allows us to define an initialization parameter for a Web application. This parameter is defined as a name-value pair that can be discovered at deployment time. Our entries in the web.xml file for defining the port numbers are:

<context-param>
    <param-name>listenPort_http</param-name>
    <param-value>7001</param-value>
</context-param>
<context-param>
    <param-name>listenPort_https</param-name>
    <param-value>7002</param-value>
</context-param>

The discovery of these values at deployment time in our application takes this form:

String httpPort = aServletContext.getInitParameter("listenPort_http");
String httpsPort = aServletContext.getInitParameter("listenPort_https");

Algorithm with configurable ports

We must modify our algorithm to allow for nonstandard server ports. The algorithm now:

  • Determines the protocol used to request our Web resource
  • If the protocol matches the protocol we want for this resource, it does nothing
  • Otherwise, it redirects to the same URL using the correct protocol, modifying the specified port number if necessary

In code, the algorithm now looks like this:

1 2 3 Page
Recommended
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more