You should use a combination of ESAPI, JSoup and JSR-303's @SafeHtml annotation to prevent XSS and filter out harmful values before they are stored.
package com.domain.security.filter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.ws.rs.core.MultivaluedMap;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Entities.EscapeMode;
import org.jsoup.safety.Whitelist;
import org.owasp.esapi.ESAPI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerRequestFilter;
public class XSSFilter implements ContainerRequestFilter
{
private static final Logger LOG = LoggerFactory.getLogger( XSSFilter.class );
/**
* @see ContainerRequestFilter#filter(ContainerRequest)
*/
@Override
public ContainerRequest filter( ContainerRequest request )
{
// Clean the query strings
cleanParams( request.getQueryParameters() );
// Clean the headers
cleanParams( request.getRequestHeaders() );
// Clean the cookies
cleanParams( request.getCookieNameValueMap() );
// Return the cleansed request
return request;
}
/**
* Apply the XSS filter to the parameters
* @param parameters
* @param type
*/
private void cleanParams( MultivaluedMap<String, String> parameters )
{
LOG.debug( "Checking for XSS Vulnerabilities: {}", parameters );
for( Map.Entry<String, List<String>> params : parameters.entrySet() )
{
String key = params.getKey();
List<String> values = params.getValue();
List<String> cleanValues = new ArrayList<String>();
for( String value : values )
{
cleanValues.add( stripXSS( value ) );
}
parameters.put( key, cleanValues );
}
LOG.debug( "XSS Vulnerabilities removed: {}", parameters );
}
/**
* Strips any potential XSS threats out of the value
* @param value
* @return
*/
public String stripXSS( String value )
{
if( value == null )
return null;
// Use the ESAPI library to avoid encoded attacks.
value = ESAPI.encoder().canonicalize( value );
// Avoid null characters
value = value.replaceAll("\0", "");
// Clean out HTML
Document.OutputSettings outputSettings = new Document.OutputSettings();
outputSettings.escapeMode( EscapeMode.xhtml );
outputSettings.prettyPrint( false );
value = Jsoup.clean( value, "", Whitelist.none(), outputSettings );
return value;
}
}
And an example of using JSR-303
import org.hibernate.validator.constraints.SafeHtml;
import org.hibernate.validator.constraints.SafeHtml.WhiteListType;
public class MySecureModel {
@SafeHtml( whitelistType = WhiteListType.NONE )
private String userInput;
}
This is an example using Jersey 1.x and 2.x which can be easily adapted / changed into a regular HTTP filter: http://codehustler.org/blog/jersey-cross-site-scripting-xss-filter-for-java-web-apps/