FileDocCategorySizeDatePackage
BadInputFilterValve.javaAPI DocExample20089Tue Jul 01 10:09:58 BST 2003com.oreilly.tomcat.valves

BadInputFilterValve

public class BadInputFilterValve extends org.apache.catalina.valves.RequestFilterValve
Filters out bad user input from HTTP requests to avoid malicious attacks including Cross Site Scripting (XSS), SQL Injection, and HTML Injection vulnerabilities, among others.
author
Jason Brittain
version
1.0

Fields Summary
protected static String
info
Descriptive information about this implementation.
protected boolean
escapeQuotes
The flag that determines whether or not to escape quotes that are part of the request.
protected boolean
escapeAngleBrackets
The flag that determines whether or not to escape angle brackets that are part of the request.
protected boolean
escapeJavaScript
The flag that determines whether or not to escape JavaScript function and object names that are part of the request.
protected HashMap
quotesHashMap
A substitution mapping (regular expression to match, replacement) that is used to replace single quotes (') and double quotes (") with escaped equivalents that can't be used for malicious purposes.
protected HashMap
angleBracketsHashMap
A substitution mapping (regular expression to match, replacement) that is used to replace angle brackets (<>) with escaped equivalents that can't be used for malicious purposes.
protected HashMap
javaScriptHashMap
A substitution mapping (regular expression to match, replacement) that is used to replace potentially dangerous JavaScript function calls with escaped equivalents that can't be used for malicious purposes.
protected int
debug
The debug level.
Constructors Summary
public BadInputFilterValve()
Construct a new instance of this class with default property values.


        super();

        // Populate the (regex, substitution) maps.
        quotesHashMap.put("\"", """);
        quotesHashMap.put("\'", "'");
        quotesHashMap.put("`", "`");
        angleBracketsHashMap.put("<", "<");
        angleBracketsHashMap.put(">", ">");
        javaScriptHashMap.put(
            "document(.*)\\.(.*)cookie", "document.cookie");
        javaScriptHashMap.put("eval(\\s*)\\(", "eval(");
        javaScriptHashMap.put("setTimeout(\\s*)\\(", "setTimeout$1(");
        javaScriptHashMap.put("setInterval(\\s*)\\(", "setInterval$1(");
        javaScriptHashMap.put("execScript(\\s*)\\(", "exexScript$1(");
        javaScriptHashMap.put("javascript:", "javascript:");
    
Methods Summary
public booleancheckAllowsAndDenies(java.lang.String property, org.apache.catalina.Request request, org.apache.catalina.Response response, org.apache.catalina.ValveContext valveContext)
Perform the filtering that has been configured for this Valve, matching against the specified request property. If the request is allowed to proceed, this method returns true. Otherwise, this method sends a Forbidden error response page, and returns false.

This method borrows heavily from RequestFilterValve.process(), only this method has a boolean return type and doesn't call valveContext.invokeNext().

param
property The request property on which to filter
param
request The servlet request to be processed
param
response The servlet response to be processed
param
context The valve context used to invoke the next valve in the current processing pipeline
exception
IOException if an input/output error occurs
exception
ServletException if a servlet error occurs
return
true if the request is still allowed to proceed.


        // Check the deny patterns, if any
        for (int i = 0; i < denies.length; i++) {
            if (denies[i].match(property)) {
                ServletResponse sres = response.getResponse();
                if (sres instanceof HttpServletResponse) {
                    HttpServletResponse hres = (HttpServletResponse) sres;
                    hres.sendError(HttpServletResponse.SC_FORBIDDEN);
                    return false;
                }
            }
        }

        // Check the allow patterns, if any
        for (int i = 0; i < allows.length; i++) {
            if (allows[i].match(property)) {
                return true;
            }
        }

        // Allow if denies specified but not allows
        if ((denies.length > 0) && (allows.length == 0)) {
            return true;
        }

        // Deny this request
        ServletResponse sres = response.getResponse();
        if (sres instanceof HttpServletResponse) {
            HttpServletResponse hres = (HttpServletResponse) sres;
            hres.sendError(HttpServletResponse.SC_FORBIDDEN);
        }
        return false;
    
public voidfilterParameters(java.util.HashMap subs, org.apache.catalina.Request request)
Filters all existing parameters for potentially dangerous content, and escapes any if they are found.

param
escapes A HashMap containing substitution regex data.
param
request The Request that contains the parameters.


        ParameterMap paramMap =
            (ParameterMap) ((HttpServletRequest) request).getParameterMap();
        // Unlock the parameters map so we can modify the parameters.
        paramMap.setLocked(false);
        try {
            // Loop through each of the substitution patterns.
            Iterator x = subs.keySet().iterator();
            while (x.hasNext()) {
                String pattern = (String) x.next();
                RE r = new RE(pattern);

                // Loop through the list of parameters.
                Iterator y = paramMap.keySet().iterator();
                while (y.hasNext()) {
                    String name = (String) y.next();
                    String[] values = ((HttpServletRequest)
                                       request).getParameterValues(name);
                    // See if the name contains the pattern.
                    boolean nameMatch;
                    synchronized (r) {
                        nameMatch = r.match(name);
                    }
                    if (nameMatch) {
                        // The parameter's name matched a pattern, so we
                        // fix it by modifying the name, adding the parameter
                        // back as the new name, and removing the old one.
                        String newName;
                        synchronized (r) {
                            newName = r.subst(name,
                                (String) subs.get(pattern));
                        }
                        ((HttpRequest) request).addParameter(newName, values);
                        paramMap.remove(name);
                        log("Parameter name " + name +
                            " matched pattern \"" + pattern +
                            "\".  Remote addr: " +
                            ((HttpServletRequest) request).getRemoteAddr());
                    }
                    // Check the parameter's values for the pattern.
                    if (values != null) {
                        for (int i = 0; i < values.length; i++) {
                            String value = values[i];
                            boolean valueMatch;
                            synchronized (r) {
                                valueMatch = r.match(value);
                            }
                            if (valueMatch) {
                                // The value matched, so we modify the value
                                // and then set it back into the array.
                                String newValue;
                                synchronized (r) {
                                    newValue = r.subst(value,
                                        (String) subs.get(pattern));
                                }
                                values[i] = newValue;
                                ((HttpRequest) request).addParameter(
                                    name, values);
                                log("Parameter \"" + name + "\"'s value \"" +
                                    value + "\" matched pattern \"" +
                                    pattern + "\".  Remote addr: " +
                                    ((HttpServletRequest)
                                    request).getRemoteAddr());
                            }
                        }
                    }
                }
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            // Make sure the parameters map is locked again when we're done.
            paramMap.setLocked(true);
        }
    
public booleangetEscapeAngleBrackets()
Gets the flag which determines whether this Valve will escape any angle brackets that are part of the request, before the request is performed.


        return escapeAngleBrackets;

    
public booleangetEscapeJavaScript()
Gets the flag which determines whether this Valve will escape any potentially dangerous references to JavaScript functions and objects that are part of the request, before the request is performed.


        return escapeJavaScript;

    
public booleangetEscapeQuotes()
Gets the flag which determines whether this Valve will escape any quotes (both double and single quotes) that are part of the request, before the request is performed.



    // ------------------------------------------------------------- Properties


                                     
       

        return escapeQuotes;

    
public java.lang.StringgetInfo()
Return descriptive information about this Valve implementation.


        return info;

    
public voidinvoke(org.apache.catalina.Request request, org.apache.catalina.Response response, org.apache.catalina.ValveContext valveContext)
Sanitizes request parameters before bad user input gets into the web application.

param
request The servlet request to be processed
param
response The servlet response to be created
param
valveContext The valve context used to invoke the next valve in the current processing pipeline
exception
IOException if an input/output error occurs
exception
ServletException if a servlet error occurs


        // Skip logging for non-HTTP requests and responses.
        if (!(request instanceof HttpRequest) ||
            !(response instanceof HttpResponse)) {
            valveContext.invokeNext(request, response);
            return;
        }

        // Only let requests through based on the allows and denies.
        if (denies.length > 0 || allows.length > 0) {
            if (processAllowsAndDenies(request, response, valveContext)) {

                // Filter the input for potentially dangerous JavaScript
                // code so that bad user input is cleaned out of the request
                // by the time Tomcat begins to perform the request.
                HashMap parameterEscapes = new HashMap();
                if (escapeQuotes) {
                    // Escape all quotes.
                    parameterEscapes.putAll(quotesHashMap);
                }
                if (escapeAngleBrackets) {
                    // Escape all angle brackets.
                    parameterEscapes.putAll(angleBracketsHashMap);
                }
                if (escapeJavaScript) {
                    // Escape potentially dangerous JavaScript method calls.
                    parameterEscapes.putAll(javaScriptHashMap);
                }
                filterParameters(parameterEscapes, request);

                // Perform the request.
                valveContext.invokeNext(request, response);
            }
        }
    
protected voidlog(java.lang.String message)
Log the specified message to our current Logger (if any).

param
message Message to be logged


        Logger logger = container.getLogger();
        if (logger != null)
            logger.log(this.toString() + ": " + message);
        else
            System.out.println(this.toString() + ": " + message);

    
public booleanprocessAllowsAndDenies(org.apache.catalina.Request request, org.apache.catalina.Response response, org.apache.catalina.ValveContext valveContext)
Uses the functionality of the (abstract) RequestFilterValve to stop requests that contain forbidden string patterns in parameter names and parameter values.

param
request The servlet request to be processed
param
response The servlet response to be created
param
ValveContext The valve context used to invoke the next valve in the current processing pipeline
exception
IOException if an input/output error occurs
exception
ServletException if a servlet error occurs
return
false if the request is forbidden, true otherwise.


        ParameterMap paramMap =
            (ParameterMap) ((HttpServletRequest) request).getParameterMap();
        // Loop through the list of parameters.
        Iterator y = paramMap.keySet().iterator();
        while (y.hasNext()) {
            String name = (String) y.next();
            String[] values = ((HttpServletRequest)
                               request).getParameterValues(name);

            // See if the name contains a forbidden pattern.
            if (!checkAllowsAndDenies(name, request, response,
                                      valveContext)) {
                return false;
            }

            // Check the parameter's values for the pattern.
            if (values != null) {
                for (int i = 0; i < values.length; i++) {
                    String value = values[i];
                    if (!checkAllowsAndDenies(value, request, response,
                                              valveContext)) {
                        return false;
                    }
                }
            }
        }

        // The request should continue.
        return true;
    
public voidsetDebug(int debug)
Set the debugging detail level for this Valve.

param
debug The new debugging detail level.


        this.debug = debug;

    
public voidsetEscapeAngleBrackets(boolean escapeAngleBrackets)
Sets the flag which determines whether this Valve will escape any angle brackets that are part of the request, before the request is performed.

param
angleBrackets


        this.escapeAngleBrackets = escapeAngleBrackets;

    
public voidsetEscapeJavaScript(boolean escapeJavaScript)
Sets the flag which determines whether this Valve will escape any potentially dangerous references to JavaScript functions and objects that are part of the request, before the request is performed.

param
escapeJavaScript


        this.escapeJavaScript = escapeJavaScript;

    
public voidsetEscapeQuotes(boolean escapeQuotes)
Sets the flag which determines whether this Valve will escape any quotes (both double and single quotes) that are part of the request, before the request is performed.

param
escapeQuotes


        this.escapeQuotes = escapeQuotes;

    
public java.lang.StringtoString()
Return a text representation of this object.


        return ("BadInputFilterValve[container=" + container.getName() + ']");