GreatWebGuy Self-proclaimed greatness is a hard thing to prove.

23Feb/0835

Simple Cross Site Scripting (XSS) Servlet Filter

Ran into some issues on some of our Java sites today and needed a quick fix to protect the sites from malicious Cross Site Scripting (XSS) attempts. If you're not aware of what XSS is and have websites that have sensitive user data, you may want to read up, you're probably vulnerable, which means your users are vulnerable. I'm not claiming this is a perfect solution, but it was easy to implement and corrected the vulnerabilities with form and url injection. We basically have a Servlet Filter that's going to intercept every request sent to the web application and then we use an HttpServletRequestWrapper to wrap and override the getParameter methods and clean any potential script injection.


Here's the Filter:


package com.greatwebguy.filter;
import java.io.IOException;
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 javax.servlet.http.HttpServletRequest;
public class CrossScriptingFilter implements Filter {
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }
    public void destroy() {
        this.filterConfig = null;
    }
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    	throws IOException, ServletException {
       	chain.doFilter(new RequestWrapper((HttpServletRequest) request), response);
    }
}

Here's the wrapper:

package com.greatwebguy.filter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public final class RequestWrapper extends HttpServletRequestWrapper {
	public RequestWrapper(HttpServletRequest servletRequest) {
		super(servletRequest);
	}
	public String[] getParameterValues(String parameter) {
	  String[] values = super.getParameterValues(parameter);
	  if (values==null)  {
                  return null;
          }
	  int count = values.length;
	  String[] encodedValues = new String[count];
	  for (int i = 0; i < count; i++) {
                 encodedValues[i] = cleanXSS(values[i]);
	   }
	  return encodedValues;
	}
	public String getParameter(String parameter) {
		  String value = super.getParameter(parameter);
		  if (value == null) {
		         return null;
                  }
		  return cleanXSS(value);
	}
	public String getHeader(String name) {
	    String value = super.getHeader(name);
	    if (value == null)
	        return null;
	    return cleanXSS(value);
	}
	private String cleanXSS(String value) {
                //You'll need to remove the spaces from the html entities below
		value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;");
		value = value.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41;");
		value = value.replaceAll("'", "& #39;");
		value = value.replaceAll("eval\\((.*)\\)", "");
		value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
		value = value.replaceAll("script", "");
		return value;
	}
}

Add this to the top of your web.xml:

    
    	XSS
    	XSS
    	
    	com.greatwebguy.filter.CrossScriptingFilter
    
    
    	XSS
    	/*
    

I'm sure the cleanXSS replacements aren't the most efficient way of doing this, you could replace it StringEscapeUtils.escapeHtml from commons lang to simplify it a little, it's up to you, it all depends on what your site is doing and whether it's going to be a pain having all the html escaped, you could also adjust the url-pattern of the filter to be more specific to your application urls, so that everything under your app isn't running through the filter.

Some things to be aware of with this approach, you'll need to account for what you've encoded or in some cases you'll end up with weird characters in your database and possibly in validation of your input boxes. Some would recommend a more positive validation rather than negative validation and only allow a certain range of characters, it's up to you, but it is something to think about.

Comments (35) Trackbacks (2)
  1. Hi, you should really reformat you source in your posting , otherwise it really looks like nonsense:
    value.replaceAll(“<”, “<”)

  2. Yeah, thanks had some issue with WordPress reformatting the text, I see the one you posted above looks great too. If you had read the post you would have found the link below the source posting with a text version of the cleanXSS method. http://greatwebguy.com/wp-content/uploads/cleanXSS.txt

  3. Hi,

    Am I missing something, or is the class variable this.filterConfig not initialized?

    Eclipse complains, and simply initializing a class variable fixes it:

    FilterConfig filterConfig = null;

    Thx,
    Bill

  4. I think by implementing Filter it has to be part of init’s signature, if you’re not using it to pass in parameters into the filter there’s probably no reason to have anything in the init method.

  5. Hi,

    Tried the filter. It caught all the input form fields but not some of the parameters as “p_p_mode” sent in the URL? Still found XSS on those parameters.

    Do not understand why. Any suggestions on how to fix?

    Thanks,

  6. hi can you please4 explain the meaning of each line in method private String cleanXSS(String value).. i mean which line is used to prevent what.

  7. I’m getting this error on my jsps after I’ve implemented this filter. Any idea what causes this?

    [12/10/08 12:38:43:289 EST] 0000003e ServletWrappe E SRVE0068E: Uncaught exception thrown in one of the service methods of the servlet: /welcome.jsp. Exception thrown : javax.servlet.ServletException: non-HTTP request or response

  8. Hi, to be shure there is no evil code in the Request the URLEncoder-Output has to be checked too: %3Cscript%3Ealert%281234%29%3C%2Fscript%3E

  9. Since line 45 replaces parathesis, line 47 will never be hit. Also, replacing all the JS events (onload, onclick, etc) is a good idea, as is making the match case insensitive.

  10. Hi

    I am interested in XSS and XSRF for protecting our servers.
    I’m glad to refer your XSS Filter.

    If input characters is “SCRIPT” or “ScRipT”, what should we do about line 49?

  11. I think the main idea here is the use of a Servlet Filter to override the getParameter methods of the HttpServletRequest with an HttpServletRequestWrapper, the cleanXSS method I’m sure is not perfect, but is welcome to improvement, you could use a more complex regex to make it case insensitive to fix the “SCript” condition

  12. Adding (?i) , in front of the replacing String would treat it as case insensitive way of replacement. So , in cleanXSS() method , you can use
    value = value.replaceAll(“(?i)script”, “”); instead of
    value = value.replaceAll(“(?i)script”, “”); for a case insensitive replacement.

    Thanks,
    -Tapas

  13. Sorry , I meant ,
    value = value.replaceAll(”(?i)script”, “”); instead of
    value = value.replaceAll(”script”, “”); for a case insensitive replacement.

    In my last comment…

  14. Is there any updated code available for cleanXSS method, considering the comments provided above?

  15. HOW CAN I USE IT IN STRUTS1.1 APPLICATION

  16. Thanks for the idea. I also had to initialize FilterConfig since looks like it’s deprecated to leave it uninitialized.

  17. value.replaceAll(“(?i)script”, “”);

    So the user input ” my description ” is filtered to “my deion” in some systems is considered fine?

  18. Does this work with GETs as well as POSTs? I’ve implemented something very similar to this that works fine for POST requests, but I get the following error from springframework when I use it for a GET request: java.lang.IllegalArgumentException: ‘source’ must not be null.

  19. In case of a webapp utilizing the NetUI framework (Struts based) where the org.apache.beehive.netui.pageflow.PageFlowRequestProcessor is defined as processorClass, you may want to implement the getParameterMap method in order to support XSS filtering of the variables submitted via POST requests through NetUI Forms. An example of the getParameterMap implementation follows.

    public Map getParameterMap() {

    Map map = super.getParameterMap();

    Iterator iter = (map.keySet()!=null)?map.keySet().iterator():null;
    String key = null;
    String[] values = null;
    if(iter!=null) {
    while(iter.hasNext()) {
    key = (String) iter.next();
    if(key!=null) {
    values = (String[])map.get(key);
    for(int i=0; i<values.length; i++)
    values[i] = cleanXSS(values[i]);
    }
    }
    }

    return map;
    }

    The above is necessary since the method of the HttpServletRequest that is used by the PageFlowRequestProcessor in order to bind the submitted values to the respective Form Bean is the getParameterMap method.

  20. Thanks for the response webguy. I noticed someone brought up the fact the word description comes up as deion (same with any other word that contains the word “script”). One way to alleviate this problem may be to use a regular expression that checks if there is at least one letter before the word script. Something like
    value = value.replaceAll(“(?<![a-zA-z])script(?![a-zA-z])|javascript", "");
    should work. Has anyone found another solution to this problem?

  21. HI,
    Tried to use this filter and it did worked for all but i had an issue.
    Actually i wantd to allow xml tags to pass through the request and not get filtered .
    Could anyone suggest what i can do to implement that….

    a sample xml tag wd be anything like :

    • @Sayba I mostly intended the article to show use of the Servlet Filter and RequestWrapper method of being able to clean harmful xss type characters out of the user input. The cleanXSS method is not hard and fast and can be expanded on or modified to suit your security needs and should probably be adjusted from time to time to account for new types of XSS attacks. So feel free to modify cleanXSS to meet your xml needs and protect your site.

  22. webguy, this has been working beautifully for my spring app, but it fails miserably when I submit a form that happens to be a multipart request. FYI – I use the @ModelAttribute notation through spring to set the particular object, which does happen to work for other non-multipart requests. Any idea how to get around this?

  23. Hi,

    I have a website application and I want to implement XSS. Can you please explain how does the above code works and how will it benefit the exisitng application? I am not very familiar with web services.

    Regards,
    Prasanth

    • @prasanth This solution is specific to Java-based web applications and uses a Servlet Filter to wrap calls to request.getParameter() so that any text that could potentially allow an XSS exploit can be dealt with appropriately. The technique is the most relevant piece, the regular expressions may need to be updated or improved as the web changes.

  24. The solution here is to block/escape the vulnerable characters. Could someone suggest a solution to encode/decode the url.

  25. The Wrapper handles only the value of a request parameter. The problem is that some server side code may ‘reflect’ a set of parameters via some Redirect action. During the ‘reflect’ action, one may find the offending code as a request parameter name, not value.

    This means the code can be improved even further by proper escaping the parameter name as well.

  26. how to do the same thing with ResponseWrapper

  27. George – your comment was a lifesaver – been using most of my day on not understanding why the netui binding didnt use the altered values from getParameter. Thanks a ton!

  28. Hi guys,
    how can I solve Cross Site scripting in liferay 5.2? the above solution doesn’t work. But is working fine in Liferay 6. Would appreciate any suggestion.
    Thanks.


Leave a comment

(required)