BadInputFilter revisited

Restore the security benefits of BadInputFilter for any Tomcat or Servlet/JSP container implementation

Designed for Tomcat 6, BadInputFilter provides a frontline of defense against common web application security exploits such as SQL injection attacks and cross-site scripting. Unfortunately, BadInputFilter breaks silently in later implementations of Tomcat. In this article, learn how to restore the security benefits of BadInputFilter for all versions of Tomcat, and even for use in other Servlet/JSP containers.

As a software developer and educator, I read a lot of technical books. Some are more useful than others, but one that I have found to be extremely useful for several years is Tomcat: The Definitive Guide (Second Edition) by Jason Brittain and Ian F. Darwin (see Resources). Even though this book was written for Tomcat 6, most of it is still applicable to Tomcat 7, currently the latest stable release. If you use Apache Tomcat as much as I do, this book is an invaluable reference. (Note to Jason and Ian: With Apache Tomcat 8 on the horizon, it’s time to consider writing a third edition!)

Brittain & Darwin provide a thorough introduction to configuring and running Apache Tomcat in production environments. The book also provides source code for a couple of useful classes named BadInputFilter and BadInputValve, which can be used to filter out potentially dangerous requests. The problem is that these two classes rely on knowledge that is specific to Tomcat 6. In the more recent versions of Tomcat, BadInputValve no longer compiles, and a part of the functionality provided by BadInputFilter no longer works. Both the original version of BadInputFilter and an updated version available on SourceForge have this problem (see Resources). Moreover, the failure of BadInputFilter is silent, in the sense that there is no indication that it no longer does part of its job.

In this article I propose an update and correction to BadInputFilter. To limit the scope of the article, I will focus on the original implementation of BadInputFilter from the Brittain & Darwin book. You could use a similar approach for the updated implementation with a few changes that are fairly straightforward. In fact, you’ll find two versions of my updated BadInputFilter in the source code that comes with this article. In the package com.softmoore.filter, BadInputFilter corresponds to the original implementation and BadInputFilter2 corresponds to the updated implementation currently hosted on SourceForge.

If you are reading this article on JavaWorld, I assume that you have programmed in Java and have some familiarity with using Java-based technology for web applications such as servlets, JSP, and so on. I also assume that you know the role that Tomcat plays in these kinds of applications. I will provide a brief introduction to filters, in order to give context for the remainder of the article.

What about BadInputValve?

You could take a similar approach to what I’ve done with BadInputFilter to restore part of the functionality of BadInputValve. You would need to do slightly more work because BadInputValve extends RequestFilterValve, a Tomcat-specific class in package org.apache.catalina.valves. RequestFilterValve’s implementation changed from Tomcat 6 to Tomcat 7, with the result that BadInputValve no longer compiles.

Filters and valves

So what is a filter? As described in the Javadoc for interface Filter (see Resources), a filter is an object that does filtering tasks either on the request to a resource (e.g., a servlet or an .html file), or on the response from a resource, or both. More than one filter can be applied in a chain to a resource or a collection of resources, which is an excellent example of the Chain of Responsibility pattern. Figure 1 illustrates the use of filters in a web application.

Figure 1. Filters in a Java web application

Examples of uses for filters include:

  1. Authentication
  2. Logging and auditing
  3. Data compression
  4. Encryption

One of the cool things about filters is that they can be added to an application after the fact. For example, suppose that you have an existing web application, and you decide that you want to log some special messages whenever requests are received for certain web resources. You can rewrite the part of the application that serves up these resources, or you can simply write a filter that intercepts requests for the resources, logs the message, and then forwards the request on to be handled in the normal manner. In the second approach, the existing web application remains unmodified except for the web application deployment descriptor, an XML file named web.xml, where you specify the existence of the filter and the resources to which it applies.

Valves are similar to filters except for three very important differences:

  1. Valves are specific to Tomcat and, therefore, run only in the Tomcat Servlet/JSP container. Filters, on the other hand, are part of the Java Servlet specification and are designed to be independent of any Servlet/JSP container, so they are portable to other Servlet/JSP containers besides Tomcat. Unfortunately the code for BadInputFilter depends on implementation details in Tomcat 6, which changed with newer versions of Tomcat.
  2. If your architecture consists of multiple web applications on the same Tomcat server, valves can be configured in one place to filter requests for all or some of them. Filters need to be configured separately for each web application.
  3. Filters are easily configured to run on specific URL patterns. Valves require that you write your own matching code for this purpose.

Using BadInputFilter for web application security

If a web application isn’t designed with security in mind, it is likely vulnerable to external security attacks such a cross-site scripting (XSS), HTML injection, and SQL injection. BadInputFilter was designed to check for potentially dangerous user input and filter out bad requests. Although you probably should take additional security measures, you can think of BadInputFilter as a frontline of defense for some well-known web application security exploits.

Essentially BadInputFilter examines user requests for potential issues. If an issue is found, it performs one of two actions—either (1) it forbids the request or (2) it escapes the bad user input. Performing these actions in a filter allows you to implement the code once and then easily apply it to multiple (or all) resources within the same web application.

Forbidding the user input is accomplished by sending an HTTP response status code of 403 (forbidden) back to the client. For example, if the user input contains certain non-printable control characters, you will likely want to deny the request altogether. The part of BadInputFilter that forbids certain characters within the user input will still work correctly. But the second part, the part that escapes bad user input, will fail.

Web application security exploits

Common web application security exploits are described in the Brittain & Darwin book as well as in the article “CWE/SANS top 25 most dangerous programming errors.” Steve Friedl also gives an excellent introduction to SQL injection in the article “SQL injection attacks by example,” a must-read for anyone using relational databases to support web applications.

Where BadInputFilter fails

A number of the web security exploits involve entering characters or phrases that have special meaning within the context of HTML, JavaScript, or SQL. For example, if user input is not properly validated or escaped, it is possible to enter JavaScript that gets executed on the server or that reveals information about the server environment. As an example of what I mean by escaping user input, you might want to look for all occurrences of left- or right-angle brackets (“<” or “>”) and replace them by equivalent HTML/XML entities (“&lt;” or “&gt;” respectively). Thus user input containing <script> (indicating possible JavaScript code) would be rendered harmless in most cases, but would still display properly if echoed back to the user as part of an HTML response page.

In their book, Brittain & Darwin provide a test JSP page (input_test.jsp) that can be used to see how various filtered and unfiltered user input is handled. Configuring BadInputFilter to filter requests to this test page shows that BadInputFilter no longer escapes user input as originally intended. The reason that BadInputFilter fails is that it relies on implementation details from Tomcat 6.

Here is a brief description, from an implementation perspective, of why BadInputFilter fails. Filters have access to user input through an object of type HttpServletRequest. For an HttpServletRequest object, the method getParameterMap() is supposed to return an immutable Map containing parameter names and values. In Tomcat 6, the immutability of this object could be bypassed by using reflection to get access to the setLocked() method and then invoking it with a parameter of false, essentially unlocking the immutable Map. With the unlocked Map, it was possible to change the parameter names and/or values. In later versions of Tomcat, using this approach to unlock and modify request parameters does not work, but the failure is silent. No indication of failure appears anywhere in the system, neither on a webpage nor in a log file. The user’s input is simply passed on unmodified—meaning that it is not escaped.

Modifying BadInputFilter to escape user input

As I’ve described, HttpServletRequest has a method named getParameterMap() that returns an immutable Map, thus mapping parameter names to values. But in order to “escape” user input, you need to be able to modify these parameter names and/or values. The question is how to modify an immutable Map. Obviously you can’t modify the Map directly (or at least you shouldn’t be able to modify the Map), but you can wrap the entire HttpServletRequest in an HttpServletRequestWrapper, which can then be passed along to the destination resource in place of the original request. The “wrapped” request copies and escapes the original parameter names and values, which the destination resource then has access to.

I’ll present the code to correct the existing problems with BadInputFilter in the form of two Java classes. The first class, FilterableRequest, extends HttpServletRequestWrapper in order to provide access and modification (escaping) of the request parameters. The constructor for FilterableRequest makes a local copy of the parameter Map of the HttpServletRequest that it wraps. It then overrides the inherited methods that provide access to the Map so that they use the local copy. Inherited methods still treat the parameter Map as immutable, but FilterableRequest also provides a new method, getModifiableParameterMap(), that allows modification of the parameters. Listing 1 is the source code for FilterableRequest.

Listing 1. FilterableRequest

package com.softmoore.filter;

   import java.util.*;
   import javax.servlet.http.*;

   /**
    * Wraps an HttpServletRequest so that parameters can
    * be modified by a filter.
    *
    * @author John I. Moore, Jr.
    */
   public class FilterableRequest extends HttpServletRequestWrapper
     {
       private Map<String, String[]> parameters = null;

       /**
        * Construct a wrapper for the original request.
        *
        * @param request the original HttpServletRequest
        */
       public FilterableRequest(HttpServletRequest request)
         {
           super(request);
           parameters = new TreeMap<String, String[]>();
           parameters.putAll(super.getParameterMap());
         }

       @Override
       public String getParameter(final String name)
         {
           String[] values = parameters.get(name);
           return values != null ? values[0] : null;
         }

       @Override
       public Map<String, String[]> getParameterMap()
         {
           return Collections.unmodifiableMap(parameters);
         }

       /**
        * Returns a ParameterMap that can be modified.
        */
       protected Map<String, String[]> getModifiableParameterMap()
         {
           return parameters;
         }

       @Override
       public Enumeration getParameterNames()
         {
           return Collections.enumeration(parameters.keySet());
         }

       @Override
       public String[] getParameterValues(final String name)
         {
           return parameters.get(name);
         }
     }

Since parts of the implementation for BadInputFilter still work correctly (for example, the initialization code and the part that forbids user input by sending an HTTP response status code of 403), the easiest way to illustrate the correction to Jason Brittain’s version of BadInputFilter is to create a new version that extends the original version and then overrides only those methods that need to change. (This approach is sometimes known as implementation inheritance.)

Source code for the new version of BadInputFilter is shown in Listing 2 below. The essence of the changes can be summarized as follows:

1 2 3 Page 1
Notice to our Readers
We're now using social media to take your comments and feedback. Learn more about this here.