This is the first post in a series covering tactics for implementing input and output chokepoints in J2EE. My goal is to describe different techniques in separate posts and then summarize the tradeoffs (advantages, disadvantages, and corner cases) involved in putting them in place in a final post. In this first post, I’ll show you how to setup a J2EE servlet filter to perform HTML escaping on multiple servlet-related input sources. A lot of DEV teams out there do this in an attempt to prevent cross-site scripting vulnerabilities.
What’s a chokepoint?
An input chokepoint is some place in the application’s design that input from one or more sources must pass through. It’s at this centralized point in the design that input validation can occur, security checks can be enforced, and other operations can be performed. Often, it’s difficult to have one chokepoint in a design that’s able to handle ALL possible input sources. It’s usually easier to implement a chokepoint for all major input types: servlets, files, DB queries, OS repos (like the Windows registry), external web API interfaces, and so on.
Input sources
While all sources of input are important to consider, to keep this post simple, we’ll only consider servlet-based input. With servlet-based input, there are multiple data structures that need to be securely handled: request parameter fields, cookie values, and request header fields immediately come to mind. Each of these structures can be manipulated by an attacker in malicious ways. For the time being, let’s focus only on request parameter fields.
J2EE filters
In J2EE, filters can be setup to capture a request and manipulate it before passing it further down the processing pipe. A traditional example is an authentication filter. An authentication filter can be setup so that for every request, the filter is executed thereby ensuring that authentication checks are always made.
Filters can be setup to apply to all requests or to very specific ones. Because filters capture actual requests and allow for them to changed (or even blocked) before forwarding them to other web resources, it’s possible to create a filter that acts as an input chokepoint for ALL web requests. That’s exactly what we’ll accomplish here. We’ll setup a filter that enumerates all request parameter fields and performs HTML output escaping on them.
Filter configuration
In the web.xml file, we have to register the filter. We do this by specifying the fully-qualified name of the class that represents the filter and specifying a mapping for which requests the filter should be applied to.
InputFilterChokepoint us.mikeware.iochokepoints.InputFilterChokepoint A filter for performing HTML output encoding of all request parameter values InputFilterChokepoint /*
Filter class
A filter must implement the javax.servlet.Filter interface. The important filter logic is placed in the doFilter method. Here’s an example of a filter that will accomplish our goal by performing HTML output escaping of all request parameter fields:
package us.mikeware.iochokepoints;
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.commons.lang.StringEscapeUtils;
public class InputFilterChokepoint implements Filter {
@Override
public void destroy() {
// method stub
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
String name;
String value;
/*
* Lookup all request parameters
*/
Enumeration allParameters = request.getParameterNames();
while(allParameters.hasMoreElements()) {
name = (String)allParameters.nextElement();
value = request.getParameter(name);
/*
* Get all of the values for this parameter
*/
String[] values = request.getParameterValues(name);
for (int i = 0; i < values.length; i++) {
/*
* Use the Apache Commons library to
* escape value for HTML
*/
value = StringEscapeUtils.escapeHtml(value);
values[i] = value;
}
}
/*
* TODO escape request header fields
*/
/*
* TODO escape cookie values
*/
/*
* TODO escape other servlet-related data structures
*/
filterChain.doFilter(request, response);
}
@Override
public void init(FilterConfig fileConfig) throws ServletException {
// method stub
}
}
Observations
(1) When this filter is enabled, all request parameter values are HTML escaped. After this filter completes, this means that request.getParameter* returns escaped data — suddenly all articles ever published saying request.getParameter* returns raw input is no longer valid. One can argue this is a good thing, but I can imagine this sort of behavior leading to confusion. If a filter like this is put in place, it *should* be written as a coding standard and communicated to the entire DEV team. Of course, a standard should only be written if it’s accepted as “good practice”.
(2) If you believe that escaping values for HTML will make all of your DOM-based injection style attacks go away (e.g., cross-site scripting), you’re wrong. HTML escaping only helps thwart injection style attacks in certain HTML contexts. For instance, if you are using untrusted input within CSS contexts, escaping specifically for CSS is required; HTML escaping methods won’t help you. And don’t forget about all the other types of contexts: XML, JavaScript, URLs, etc.
(3) A final important point to remember is that J2EE filters are executed prior to frameworks like Struts getting a handle on the data. So, with this filter in place, all Struts ActionForms will get populated with HTML escaped values. Pretty cool, unless this new behavior breaks any validation routines you already have in place.
With little effort, this filter can be modified to handle other input sources, such as request headers and cookie values.
My take
(1) Servlet filters are useful, but they shouldn’t be used as a mechanism for preventing cross-site scripting attacks. In a perfect world, a better approach is to do strict, whitelist-based input validation on *all* externally controlled input fields.
(2) What if the “<” (and other HTML entities) is a valid character? In that case, you’re forced to fully process it and do render the output in a secure way (output escaping).
In the next post in this series, I’ll show you how to insert an input chokepoint in the Apache Struts processor.
Post a Comment