Spring Boot intercepts XSS using filters

The target

Intercept XSS using spring Boot’s Filter

tool

  • Spring boot 2.0
  • Jsoup (optional)

Realize the principle of

The Spring Boot Filter intercepts the front-end parameters and filters them. .

Basically, there are two functions: parameter blocking and script filtering.

Parameters to intercept

To filter XSS, you first need to be able to intercept front-end parameters.

Write a first Filter:

1import java.io.IOException; 2 3import javax.servlet.Filter; 4import javax.servlet.FilterChain; 5import javax.servlet.FilterConfig; 6import javax.servlet.ServletException; 7import javax.servlet.ServletRequest; 8import javax.servlet.ServletResponse; 9import javax.servlet.http.HttpServletRequest; 10 11public class XSSEscapeFilter implements Filter { 12 13 14 @Override 15 public void init(FilterConfig filterConfig) throws ServletException { 16 17 } 18 19 @Override 20 public void destroy() { 21 22 } 23 24 @Override 25 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {26 / behind/XssHttpServletRequestWrapper code. . This class is his own definition of 27 chain doFilter (new XssHttpServletRequestWrapper ((it) request), response); 28}} 30 29Copy the code

The Filter can intercept to the request, but, if you want to modify the parameters will need to redefine HttpServletRequestWrapper, only with a custom HttpServletRequestWrapper can modify parameters.

The following definitions XssHttpServletRequestWrapper:

1 2import org.apache.commons.lang3.StringUtils; 3import org.jsoup.Jsoup; 4import org.jsoup.nodes.Document; 5import org.jsoup.safety.Whitelist; 6 7import javax.servlet.ReadListener; 8import javax.servlet.ServletInputStream; 9import javax.servlet.http.HttpServletRequest; 10import javax.servlet.http.HttpServletRequestWrapper; 11import java.io.*; 12import java.util.HashMap; 13import java.util.Iterator; 14import java.util.Map; 18 * Create by zdRan on 2018/5/8 19 * 20 * @author [email protected] 21 */ 22public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { 23 private HttpServletRequest orgRequest = null; 24 25 public XssHttpServletRequestWrapper(HttpServletRequest request) { 26 super(request); 27 orgRequest = request; 31 31 @override 32 public String getParameter(String name) {33 return name; 37} 37 @override 38 public Map getParameterMap() {39 return super.getparametermap (); 41 } 42 43 @Override 44 public String[] getParameterValues(String name) { 45 String[] arr = super.getParameterValues(name); 46 // Modify the parameters. 47 return arr; 51} 50 @override 51 public String getHeader(String name) {52 return super.getheader (name); 61 public HttpServletRequest getOrgRequest() {61 public HttpServletRequest getOrgRequest() {62 return orgRequest; 61} 64 public static HttpServletRequest 64 * @return 69 */ 70 public static HttpServletRequest getOrgRequest(HttpServletRequest req) { 71 if (req instanceof XssHttpServletRequestWrapper) { 72 return ((XssHttpServletRequestWrapper) req).getOrgRequest(); 73 } 74 75 return req; 76}Copy the code

This allows you to modify the parameters, but the current situation does not allow you to handle POST requests or RequestBody annotations.

When using the RequestBody annotation, you will notice that none of the methods overwritten are left, indicating that we have not overwritten the whole method.

A bit of digging found that the RequestBody annotation reads the parameter using getInputStream().

Let’s rewrite this method:

1 @Override 2 public ServletInputStream getInputStream() throws IOException { 3 4 BufferedReader br = new BufferedReader(new InputStreamReader(orgRequest.getInputStream())); 5 String line = br.readLine(); 6 String result = ""; 7 if (line ! 8 = null) {/ / to deal with parameter 9} 10 11 return new WrappedServletInputStream (new ByteArrayInputStream (result) getBytes ())); 12}Copy the code

Then start the Filter

1 2import org.springframework.boot.web.servlet.FilterRegistrationBean; 3import org.springframework.context.annotation.Bean; 4import org.springframework.context.annotation.Configuration; 5 6import javax.servlet.DispatcherType; 7 8/** 9 * Create by zdRan on 2018/5/8 10 * 11 * @author [email protected] 12 */ 13@Configuration 14public class XssFilterConfiguration {15 /** 16 * XSS filter interceptor 17 */ 18 @bean 19 Public FilterRegistrationBean xssFilterRegistrationBean() { 20 FilterRegistrationBean initXssFilterBean = new FilterRegistrationBean(); 21 initXssFilterBean.setFilter(new XSSEscapeFilter()); 22 initXssFilterBean.setOrder(1); 23 initXssFilterBean.setEnabled(true); 24 initXssFilterBean.addUrlPatterns("/*"); 25 initXssFilterBean.setDispatcherTypes(DispatcherType.REQUEST); 26 return initXssFilterBean; 27}} 28, 29Copy the code

At this point, the parameters are basically intercepted, and you can define your own rules for modifying the parameters. XSS can also be filtered using JSOUP

Script filtering

Use JSOUP to filter labels in parameters to add dependencies

1<dependency>
2    <groupId>org.jsoup</groupId>
3    <artifactId>jsoup</artifactId>
4    <version>1.11.3</version>
5</dependency>Copy the code

Complete XssHttpServletRequestWrapper code:

1import org.apache.commons.lang3.StringUtils; 2import org.jsoup.Jsoup; 3import org.jsoup.nodes.Document; 4import org.jsoup.safety.Whitelist; 5 6import javax.servlet.ReadListener; 7import javax.servlet.ServletInputStream; 8import javax.servlet.http.HttpServletRequest; 9import javax.servlet.http.HttpServletRequestWrapper; 10import java.io.*; 11import java.util.HashMap; 12import java.util.Iterator; 13import java.util.Map; 17 * Create by zdRan on 2018/5/8 18 * 19 * @author [email protected] 20 */ 21public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { 22 private HttpServletRequest orgRequest = null; 23 /** 24 * Specifies the Whitelist that can be filtered. 25 * / 26 Private static final Whitelist Whitelist = new Whitelist(); 29 */ 30 Private static final Document.OutputSettings OutputSettings = new Document.OutputSettings().prettyPrint(false); 31 32 public XssHttpServletRequestWrapper(HttpServletRequest request) { 33 super(request); 34 orgRequest = request; 35 36 } 37 38 @Override 39 public ServletInputStream getInputStream() throws IOException { 40 41 BufferedReader br = new  BufferedReader(new InputStreamReader(orgRequest.getInputStream())); 42 String line = br.readLine(); 43 String result = ""; 44 if (line ! = null) { 45 result += clean(line); 46 } 47 48 return new WrappedServletInputStream(new ByteArrayInputStream(result.getBytes())); 49} 50 51 /** 52 * override getParameter to XSS filter both parameter names and parameter values. 53 * If you want to get the original value, By super. GetParameterValues (name) to obtain 54 * getParameterNames, getParameterValues and getParameterMap may also need to cover 55 * / 56 @ Override  57 public String getParameter(String name) { 58 if (("content".equals(name) || name.endsWith("WithHtml"))) { 59 return super.getParameter(name); 60 } 61 name = clean(name); 62 String value = super.getParameter(name); 63 if (StringUtils.isNotBlank(value)) { 64 value = clean(value); 65 } 66 return value; 67 } 68 69 @Override 70 public Map getParameterMap() { 71 Map map = super.getParameterMap(); 72 // Returned value Map 73 Map<String, String> returnMap = new HashMap<String, String>(); 74 Iterator entries = map.entrySet().iterator(); 75 Map.Entry entry; 76 String name = ""; 77 String value = ""; 78 while (entries.hasNext()) { 79 entry = (Map.Entry) entries.next(); 80 name = (String) entry.getKey(); 81 Object valueObj = entry.getValue(); 82 if (null == valueObj) { 83 value = ""; 84 } else if (valueObj instanceof String[]) { 85 String[] values = (String[]) valueObj; 86 for (int i = 0; i < values.length; i++) { 87 value = values[i] + ","; 88 } 89 value = value.substring(0, value.length() - 1); 90 } else { 91 value = valueObj.92 [native code] 93}">toString(); 94 } 95 returnMap.put(name, clean(value).trim()); 96 } 97 return returnMap; 98 } 99 100 @Override 101 public String[] getParameterValues(String name) { 102 String[] arr = super.getParameterValues(name); 103 if (arr ! = null) { 104 for (int i = 0; i < arr.length; i++) { 105 arr[i] = clean(arr[i]); 106 } 107 } 108 return arr; 109} 110 111 112 /** 113 * Override getHeader to XSS filter both parameter names and parameter values. 114 * If you want to get the original value, 116 */ 117 @override 118 public String getHeader(String name) 118 public String getHeader(String name) { 119 120 name = clean(name); 121 String value = super.getHeader(name); 122 if (StringUtils.isNotBlank(value)) { 123 value = clean(value); 124 } 125 return value; Public HttpServletRequest getOrgRequest() {123} public HttpServletRequest getOrgRequest() {123} public HttpServletRequest getOrgRequest() {123} public HttpServletRequest getOrgRequest() {123} public HttpServletRequest getOrgRequest() {123 return orgRequest; Public static HttpServletRequest */ 142 public static HttpServletRequest */ 142 public static HttpServletRequest */ 142 public static HttpServletRequest */ 142 getOrgRequest(HttpServletRequest req) { 143 if (req instanceof XssHttpServletRequestWrapper) { 144 return ((XssHttpServletRequestWrapper) req).getOrgRequest(); 145 } 146 147 return req; 148 } 149 150 public String clean(String content) { 151 String result = Jsoup.clean(content, "", whitelist, outputSettings); 152 return result; 153 } 154 155 private class WrappedServletInputStream extends ServletInputStream { 156 public void setStream(InputStream  stream) { 157 this.stream = stream; 158 } 159 160 private InputStream stream; 161 162 public WrappedServletInputStream(InputStream stream) { 163 this.stream = stream; 164 } 165 166 @Override 167 public int read() throws IOException { 168 return stream.read(); 169 } 170 171 @Override 172 public boolean isFinished() { 173 return true; 174 } 175 176 @Override 177 public boolean isReady() { 178 return true; 179 } 180 181 @Override 182 public void setReadListener(ReadListener readListener) { 183 184 } 185 } 186}Copy the code

All right. That’s the end of it, but there’s just one small problem.

Using Jsoup allows you to filter out all HTML tags, but there is a problem, for example

Parameter is: {” name “:” < HTML “, “passwd” : “12345”}, the filtered result: {” name “:”

Because the end of the tag is not found, all subsequent parameters are filtered out.

This will cause an exception when the controller gets parameters.

Link to this article: zdran.com/20180511.ht…