Spring boot использует jsoup для перехвата XSS

Spring Boot XSS

Spring boot использует фильтр для перехвата XSS

Цель

Используйте Spring Boot Filter для перехвата XSS

инструмент

  • spring boot 2.0
  • jsoup (необязательно)

Принцип реализации

Фильтр Spring boot перехватывает параметры и фильтры внешнего интерфейса (разве это не выглядит очень просто??).

Грубо говоря, есть две функции: перехват параметров и фильтрация скриптов.

перехват параметров

Если вы хотите фильтровать XSS, вы должны сначала уметь перехватывать параметры внешнего интерфейса.

Сначала напишите фильтр:

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        //后面会有 XssHttpServletRequestWrapper 的代码。这个类是自己定义的
27        chain.doFilter(new XssHttpServletRequestWrapper((HttpServletRequest) request), response);
28    }
29}
30

Этот фильтр может перехватить запрос, но если вы хотите изменить параметры, вам нужно переопределить HttpServletRequestWrapper. Только пользовательский HttpServletRequestWrapper может изменять параметры.

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;
15
16/**
17 * 实现XSS过滤
18 * Create by zdRan on 2018/5/8
19 *
20 * @author cm.zdran@gmail.com
21 */
22public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
23    private HttpServletRequest orgRequest = null;
24
25    public XssHttpServletRequestWrapper(HttpServletRequest request) {
26        super(request);
27        orgRequest = request;
28
29    }
30
31    @Override
32    public String getParameter(String name) {
33           // 对参数进行修改
34        return name;
35    }
36
37    @Override
38    public Map getParameterMap() {
39           // 对参数进行修改
40        return super.getParameterMap();;
41    }
42
43    @Override
44    public String[] getParameterValues(String name) {
45        String[] arr = super.getParameterValues(name);
46        // 对参数进行修改
47        return arr;
48    }
49
50    @Override
51    public String getHeader(String name) {
52        //对参数进行修改
53        return super.getHeader(name);;
54    }
55
56    /**
57     * 获取最原始的request
58     *
59     * @return
60     */
61    public HttpServletRequest getOrgRequest() {
62        return orgRequest;
63    }
64
65    /**
66     * 获取最原始的request的静态方法
67     *
68     * @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    }

Таким образом, параметры могут быть изменены, но текущая ситуация не может обрабатывать запросы POST или аннотации RequestBody.

При использовании аннотации RequestBody вы обнаружите, что ни один из переписанных методов не исчез, что указывает на то, что мы не переписали все методы.

Нашел немного информации и нашел:Метод чтения параметров аннотации RequestBody — getInputStream()..

Перепишем этот метод:

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 != null) {
8            //对参数进行处理
9        }
10
11        return new WrappedServletInputStream(new ByteArrayInputStream(result.getBytes()));
12    }

Затем запустите этот фильтр

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 cm.zdran@gmail.com
12 */
13@Configuration
14public class XssFilterConfiguration {
15    /**
16     * xss过滤拦截器
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}
29

На этом этапе параметры в основном перехватываются, и вы можете самостоятельно определить правила модификации параметров. Вы также можете использовать jsoup для фильтрации XSS.

Фильтрация скриптов

Используйте jsoup для фильтрации тегов в параметрах
добавить зависимости

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

Полный код XssHttpServletRequestWrapper:

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;
14
15/**
16 * 实现XSS过滤
17 * Create by zdRan on 2018/5/8
18 *
19 * @author cm.zdran@gmail.com
20 */
21public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
22    private HttpServletRequest orgRequest = null;
23    /**
24    * 配置可以通过过滤的白名单
25    * /
26    private static final Whitelist whitelist = new Whitelist();
27    /**
28     * 配置过滤化参数,不对代码进行格式化
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     * 覆盖getParameter方法,将参数名和参数值都做xss过滤。

53     * 如果需要获得原始的值,则通过super.getParameterValues(name)来获取

54     * getParameterNames,getParameterValues和getParameterMap也可能需要覆盖
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        // 返回值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     * 覆盖getHeader方法,将参数名和参数值都做xss过滤。

114     * 如果需要获得原始的值,则通过super.getHeaders(name)来获取

115     * getHeaderNames 也可能需要覆盖
116     */
117    @Override
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;
126    }
127
128    /**
129     * 获取最原始的request
130     *
131     * @return
132     */
133    public HttpServletRequest getOrgRequest() {
134        return orgRequest;
135    }
136
137    /**
138     * 获取最原始的request的静态方法
139     *
140     * @return
141     */
142    public static HttpServletRequest 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}

Отлично. На этом все закончилось, но осталась небольшая проблема.

С помощью jsoup можно отфильтровать все html-теги, но есть и такая проблема, как

Параметры: {"name":"

Поскольку конечная позиция тега не найдена, все последующие параметры будут отфильтрованы.

Это вызовет исключение, когда контроллер получит параметры.

Пожалуйста, укажите источник
Ссылка на эту статью:Этот курс.com/20180511.Контракт…