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.Контракт…