[Java] Quarantining Request Parameters

This is a follow-up on an earlier post in which I described a method for modifying HTTP request parameters on the fly in a servlet/web application. I mentioned that the technique could be used to provide a filter that automatically quarantines any potentially unsafe parameters so that application code/business logic does not need to worry about such things as XSS and SQL injection attacks, but I didn’t provide any complete reference code for such a filter. So today I aim to rectify that omission.

As this example code builds upon the OverridableHttpRequest class discussed in the previous post, you will need that class (or your own comparable implementation) before you get started. Once you have that, the code for the filter implementation is not very complex:

public class InputSanitizerFilter implements Filter {
	private static final Logger LOG = Logger.getLogger(InputSanitizerFilter.class);
	
	private static final String BANNED_INPUT_CHARS = ".*[^a-zA-Z0-9\\@\\'\\,\\.\\/\\(\\)\\+\\=\\-\\_\\[\\]\\{\\}\\^\\!\\*\\&\\%\\$\\:\\;\\? \\t]+.*";
	
	public static final String QUARANTINE_ATTRIBUTE_NAME = "filter.quarantined.params";
	public static final String SUSPICIOUS_REQUEST_FLAG_NAME = "filter.suspicious.request";

	@Override
	public void destroy() {
		//no work necessary
	}

	@Override
	@SuppressWarnings("unchecked")
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		//wrap the original request and set up an empty quarantine map
		OverridableHttpRequest newRequest = new OverridableHttpRequest((HttpServletRequest)request);
		Map<String, String> quarantine = new HashMap<String, String>();
		newRequest.setAttribute(QUARANTINE_ATTRIBUTE_NAME, quarantine);
		
		//inspect each parameter, and move any suspicious ones into thw quarantine area
		Enumeration<String> names = request.getParameterNames();
		while (names.hasMoreElements()) {
			String name = names.nextElement();
			String value = request.getParameter(name);
			if (value.matches(BANNED_INPUT_CHARS)) {
				//uh-oh, found something that doesn't look right, quarantine it and make sure the request is flagged as suspicious
				LOG.warn("Removing potentially malicious parameter from request:  " + name);
				quarantine.put(name, value);
				newRequest.removeParameter(name);
				newRequest.setAttribute(SUSPICIOUS_REQUEST_FLAG_NAME, "true");
			}
		}
		
		//done, send the modified request on down the chain
		chain.doFilter(newRequest, response);
	}

	@Override
	public void init(FilterConfig arg0) throws ServletException {
		//no work necessary
	}
}

Basically there is a regex that defines the set of disallowed characters (anything that is not a letter, number, standard punctuation character, or whitespace…this example will also exclude any parameters that contain newlines as the webapp that it is used in does not contain any inputs that can accept newlines, but you can obviously modify it to your liking), and every parameter in the request is inspected to see if it contains one or more banned character(s). Any parameter that is found to include banned characters is copied into the ‘quarantine‘ map, and then removed as a parameter (so calling ‘request.getParameter(“badParam”)‘ will return null). This will prevent application code from ever seeing the suspect parameter(s), unless it explicitly goes looking inside of the quarantine map (which you might do if you want to allow users to have special characters in their password, for instance).

Also, if one or more parameters are quarantined, the request is flagged as suspicious by setting an attribute named ‘filter.suspicious.request‘. This gives code downstream from the filter a quick way to see if the request has been modified by the filter in any way, and allows the application to make its own decisions about whether or not it wants to venture into the quarantine area to inspect the potentially unsafe data. For instance:

if (request.getAttribute(InputSanitizerFilter.SUSPICIOUS_REQUEST_FLAG_NAME) != null) {
    Map<String, String> quarantine = (Map<String, String>)request.getAttribute(InputSanitizerFilter.QUARANTINE_ATTRIBUTE_NAME);
    for (String key : quarantine.keySet()) {
        System.out.println("Quarantined parameter:  name=" + key + ", value=" + quarantine.get(key));
    }
}

…or, with the right frameworks in place you might easily create something like an ‘@IgnoresQuarantine‘ annotation that can be applied selectively to business methods to mark a parameter (or set of parameters) as not subject to quarantine on calls to that method so that you never need to manually go looking through the quarantine area (after implementing the annotation and its backing logic). But that’s just slightly outside of the scope for today.

This entry was posted in coding, java, servlet and tagged , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>