Using springMVC to prevent xss injection through Filter implementation

  • 2021-10-25 06:32:45
  • OfStack

springMVC Filter prevents xss injection

Cross-site scripting tool (cross, scripting) is abbreviated to XSS in order not to be confused with the abbreviations of cascading style sheets (cascading, style, sheets, CSS).

Malicious attackers insert malicious scriptScript code into web page. When users browse this page, Script code embedded in Web will be executed, thus achieving the purpose of malicious attack on users.

The simple prevention of XSS attack is to remove 1 sensitive script command from 1 parameter in Request request.

Originally, it was intended to be realized through HandlerInterceptor mechanism of springMVC. By obtaining request and then modifying the parameters in request, although the value was modified, the value obtained in Controller was not modified. There is no way to finish it with Filter.

Simply put, create a new httpRequest class XsslHttpServletRequestWrapper, and then override some get methods (XSS judgment prevention for parameters when getting parameters).


@WebFilter(filterName="xssMyfilter",urlPatterns="/*") 
public class MyXssFilter implements Filter{ 
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {		
	}
 
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
	        throws IOException, ServletException {
		XsslHttpServletRequestWrapper xssRequest = new XsslHttpServletRequestWrapper((HttpServletRequest)request);
		chain.doFilter(xssRequest , response); 
	}
	
	@Override
	public void destroy() {		
	}	
}

The filtering of XSS code is implemented in XsslHttpServletRequestWrapper, which mainly covers the implementation of getParameter, getParameterValues and getHeader, and then carries out XSS processing on the obtained value value.


public class XsslHttpServletRequestWrapper extends HttpServletRequestWrapper { 
  HttpServletRequest xssRequest = null;  
 public XsslHttpServletRequestWrapper(HttpServletRequest request) {
  super(request);
  xssRequest = request;
 } 
 
  @Override  
  public String getParameter(String name) {  
       String value = super.getParameter(replaceXSS(name));  
         if (value != null) {  
             value = replaceXSS(value);  
         }  
         return value;  
  }  
  
  @Override
 public String[] getParameterValues(String name) {
   String[] values = super.getParameterValues(replaceXSS(name));
   if(values != null && values.length > 0){
    for(int i =0; i< values.length ;i++){
     values[i] = replaceXSS(values[i]);
    }
   }
  return values;
  }
  
  @Override  
  public String getHeader(String name) {  
   
         String value = super.getHeader(replaceXSS(name));  
         if (value != null) {  
             value = replaceXSS(value);  
         }  
         return value;  
     } 
  /**
   *  Remove to-be-taped script , src Escape the replaced statement value Value 
   */
 public static String replaceXSS(String value) {
     if (value != null) {
         try{
          value = value.replace("+","%2B");   //'+' replace to '%2B'
          value = URLDecoder.decode(value, "utf-8");
         }catch(UnsupportedEncodingException e){
         }catch(IllegalArgumentException e){
     }
         
   // Avoid null characters
   value = value.replaceAll("\0", "");
 
   // Avoid anything between script tags
   Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE);
   value = scriptPattern.matcher(value).replaceAll("");
 
   // Avoid anything in a src='...' type of e­xpression
   scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
   value = scriptPattern.matcher(value).replaceAll("");
 
   scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
   value = scriptPattern.matcher(value).replaceAll("");
 
   // Remove any lonesome </script> tag
   scriptPattern = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE);
   value = scriptPattern.matcher(value).replaceAll("");
 
   // Remove any lonesome <script ...> tag
   scriptPattern = Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
   value = scriptPattern.matcher(value).replaceAll("");
 
   // Avoid eval(...) e­xpressions
   scriptPattern = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
   value = scriptPattern.matcher(value).replaceAll("");
 
   // Avoid e­xpression(...) e­xpressions
   scriptPattern = Pattern.compile("e­xpression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
   value = scriptPattern.matcher(value).replaceAll("");
 
   // Avoid javascript:... e­xpressions
   scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
   value = scriptPattern.matcher(value).replaceAll("");
   // Avoid alert:... e­xpressions
   scriptPattern = Pattern.compile("alert", Pattern.CASE_INSENSITIVE);
   value = scriptPattern.matcher(value).replaceAll("");
   // Avoid  O nl O ad= e­xpressions
   scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
   value = scriptPattern.matcher(value).replaceAll("");
   scriptPattern = Pattern.compile("vbscript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);  
   value = scriptPattern.matcher(value).replaceAll("");
  }         
     return filter(value);
 }  
  /**
   *  Filter special characters 
   */
  public static String filter(String value) {
         if (value == null) {
             return null;
         }        
         StringBuffer result = new StringBuffer(value.length());
         for (int i=0; i<value.length(); ++i) {
             switch (value.charAt(i)) {
              case '<':
                  result.append("<");
                  break;
              case '>': 
                  result.append(">");
                  break;
              case '"': 
                  result.append(""");
                  break;
              case '\'': 
                  result.append("'");
                  break;
              case '%': 
                  result.append("%");
                  break;
              case ';': 
                  result.append(";");
                  break;
           case '(': 
                  result.append("(");
                  break;
              case ')': 
                  result.append(")");
                  break;
              case '&': 
                  result.append("&");
                  break;
              case '+':
                  result.append("+");
                  break;
              default:
                  result.append(value.charAt(i));
                  break;
          }  
         }
         return result.toString();
     } 
}

SpringMVC Prevent XSS Tool (General Mode)

Requirements:

xss Filter Request Parameters: Content-Type is json (application/json)

SpringMVC for application/json conversion processing instructions:

spring mvc uses MappingJackson2HttpMessageConverter converter by default,

And it uses jackson to serialize objects, if we can modify the serialization and deserialization process of jackson, add filtering xss code, and register it in MappingJackson2HttpMessageConverter

Specific implementation function code:


import java.io.IOException;
import org.apache.commons.text.StringEscapeUtils;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
 
/**
 *  Deserialization 
 *
 */
public class XssDefaultJsonDeserializer extends StdDeserializer<String> { 
 public XssDefaultJsonDeserializer(){
  this(null);
 }
 
 public XssDefaultJsonDeserializer(Class<String> vc) {
  super(vc);
 }
 
 @Override
 public String deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
  // TODO Auto-generated method stub
  //return StringEscapeUtils.escapeEcmaScript(jsonParser.getText());
  return StringEscapeUtils.unescapeHtml4(jsonParser.getText());
 } 
}

SpringMVC configuration object:


@Configuration
@EnableWebMvc
public class SpingMVCConfig extends WebMvcConfigurerAdapter {
 @Override
 public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
  super.configureMessageConverters(converters);
  // TODO Auto-generated method stub
  SimpleModule module = new SimpleModule();
  //  Deserialization 
  module.addDeserializer(String.class, new XssDefaultJsonDeserializer());
  //  Serialization 
  module.addSerializer(String.class, new XssDefaultJsonSerializer());
  ObjectMapper mapper = Jackson2ObjectMapperBuilder.json().build();
  //  Register custom serializers and deserializers 
  mapper.registerModule(module);
  MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(mapper);
  converters.add(converter);
          }
}

Related articles: