我是靠谱客的博主 犹豫鸡,这篇文章主要介绍springboot中使用过滤器,jsoup过滤XSS脚本详解,现在分享给大家,希望可以做个参考。

springboot使用过滤器,jsoup过滤XSS脚本

  • 背景:略
  • 目标:完成request请求中的脚本过滤
  • 技术:filter,jsoup,requestWapper

1.把可能包含脚本的参数位置分析一下

  • post/put/delete: 请求的参数中,有可能是表单提交、也有可能是使用了@requestBody注解,那么参数就是json格式,位于request的流中。
  • get/options等:可能存在于url参数中,也有可能是表单提交的预请求中,所以一般在能想到的位置都有可能存在,包括header中。

2.分析实现过程

2.1首先要从request请求中将各个需要过滤位置的参数取出来

2.2然后将参数取出来进行过滤

2.3将过滤后的参数重新包装成request传递下去

2.4在这期间,

  • 需要准备好jsoup过滤脚本的工具类;
  • 需要自定义一个过滤器,并且在过滤器中添加匹配条件,如:那些url不需要过滤,那些请求方式必须进行过滤;
  • 对过滤器进行配置,是否开启,设置在整个过滤器链中的位置,设置过滤的白名单或者黑名单
  • 所以就很清晰了我们过滤需要哪些类,哪些配置了

一个filter

一个requestWapper

一个jsoup工具类

一个filter的配置类

2.5进行数据测试

3.代码实现过程

3.1.jsoup依赖:

复制代码
1
2
3
4
5
6
<!--screen xss --> <dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.9.2</version> </dependency>

3.2jsoup工具类:JsoupUtil

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.safety.Whitelist; import java.io.FileNotFoundException; import java.io.IOException; /** * @Auther: qianshanmuxue * @Date: 2019/2/27 19:32 * @Description: xss Illegal label filtering */ public class JsoupUtil { private static final Whitelist whitelist = Whitelist.simpleText();//jsoup白名单种类,有四种,每一种针对的标签类型不一样,具体的可以ctrl+左键点击simpleText,在jsoup源码中有响应的注释和标签名单 //add myself white list label private static final Document.OutputSettings outputSettings = new Document.OutputSettings().prettyPrint(false); static { whitelist.addAttributes(":all", "style").addTags("p").addTags("strong");//将自定义标签添加进白名单,除开白名单之外的标签都会被过滤 whitelist.preserveRelativeLinks(true);//这个配置的意思的过滤如果找不到成对的标签,就只过滤单个标签,而不用把后面所有的文本都进行过滤。 //(之前在这个问题上折腾了很久,当<script>标签只有一个时,会<script>标签后面的数据全部过滤) } public static String clean(String content) { //过滤方法 return Jsoup.clean(content, "", whitelist, outputSettings); } //test main public static void main(String[] args) throws FileNotFoundException, IOException { String text = "<a href="http://www.baidu.com/a" onclick="alert(1);"><strong><p>sss</p></strong></a><script>alert(0);</script>sss"; System.out.println(clean(text)); } }

3.3request包装类XssHttpServletRequestWrapper

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import java.io.*; import java.util.*; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import com.xxx.utils.JsoupUtil; import org.jsoup.nodes.Document; import org.springframework.util.StringUtils; /** * @Auther: qianshanmuxue * @Date: 2019/2/27 16:24 * @Description:request wapper use to get request parameter and request bdoy data and wapper another request */ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { //因为我们需要获取request中的数据,所以需要继承java底层中HttpServletRequestWrapper这个类,重写父类中的某些方法,获取相应位置的参数 private HttpServletRequest orgRequest = null; private static final Document.OutputSettings outputSettings = new Document.OutputSettings().prettyPrint(false); public XssHttpServletRequestWrapper(HttpServletRequest request) { super(request); orgRequest = request; } @Override public ServletInputStream getInputStream() throws IOException {//get BufferedReader br = new BufferedReader(new InputStreamReader(orgRequest.getInputStream())); String line = br.readLine(); String result = ""; if (line != null) { result += clean(line); } return new WrappedServletInputStream(new ByteArrayInputStream(result.getBytes())); } @Override public String getParameter(String name) { if (("content".equals(name) || name.endsWith("WithHtml"))) { return super.getParameter(name); } name = clean(name); String value = super.getParameter(name); if (!StringUtils.isEmpty(value)) { value = clean(value); } return value; } @Override public Map getParameterMap() { Map map = super.getParameterMap(); // 返回值Map Map<String, String> returnMap = new HashMap<String, String>(); Iterator entries = map.entrySet().iterator(); Map.Entry entry; String name = ""; String value = ""; while (entries.hasNext()) { entry = (Map.Entry) entries.next(); name = (String) entry.getKey(); Object valueObj = entry.getValue(); if (null == valueObj) { value = ""; } else if (valueObj instanceof String[]) { String[] values = (String[]) valueObj; for (int i = 0; i < values.length; i++) { value = values[i] + ","; } value = value.substring(0, value.length() - 1); } else { value = valueObj.toString(); } returnMap.put(name, clean(value).trim()); } return returnMap; } @Override public String[] getParameterValues(String name) { String[] arr = super.getParameterValues(name); if (arr != null) { for (int i = 0; i < arr.length; i++) { arr[i] = clean(arr[i]); } } return arr; } /** * get org request * * @return */ public HttpServletRequest getOrgRequest() { return orgRequest; } /** * wapper request */ public static HttpServletRequest getOrgRequest(HttpServletRequest req) { if (req instanceof XssHttpServletRequestWrapper) { return ((XssHttpServletRequestWrapper) req).getOrgRequest(); } return req; } public String clean(String content) { String result = JsoupUtil.clean(content); return result; } private class WrappedServletInputStream extends ServletInputStream { public void setStream(InputStream stream) { this.stream = stream; } private InputStream stream; public WrappedServletInputStream(InputStream stream) { this.stream = stream; } @Override public int read() throws IOException { return stream.read(); } @Override public boolean isFinished() { return true; } @Override public boolean isReady() { return true; } @Override public void setReadListener(ReadListener readListener) { } } }

3.4filter-XssFilter

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.StringUtils; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @Auther: qianshanmuxue * @Date: 2019/2/27 16:25 * @Description: */ //@WebFilter //@Component 在这里可以不用这个注解,以为后面我们会在config中去配置这个filter,在这里只需要实现 Filter 接口实现相应的方法就ok public class XssFilter implements Filter { private static boolean IS_INCLUDE_RICH_TEXT = false;//用于接收配置中的参数,决定这个过滤器是否开启 public List<String> excludes = new ArrayList<String>();//用于接收配置中的参数,决定哪些是不需要过滤的url(在这里,也可以修改handleExcludeURL()方法中相应的代码,使其变更为只需要过滤的url) @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; if (handleExcludeURL(req, resp)) { chain.doFilter(request, response); return; } XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request); chain.doFilter(xssRequest, response); } /** *此方法是决定对当前url是否执行过滤, *在这里没有使用请求方法(post/put)来匹配,因为在本项目中使用url匹配更适合(因为get和其他请求方式也需要进行过滤),如果你有兴趣可以把这个方法更改为匹配请求方法进行过滤 **/ private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) { if ((excludes == null || excludes.isEmpty())&&IS_INCLUDE_RICH_TEXT) { return false; } String url = request.getServletPath(); for (String pattern : excludes) { Pattern p = Pattern.compile("^" + pattern); Matcher m = p.matcher(url); if (m.find()) { return true; } } return false; } /** *过滤器初始化,从配置类中获取参数,用于初始化两个参数(是否开启,排除指定的url list) * */ @Override public void init(FilterConfig arg0) throws ServletException { String isIncludeRichText = arg0.getInitParameter("isIncludeRichText"); if (StringUtils.isNotBlank(isIncludeRichText)) { IS_INCLUDE_RICH_TEXT = BooleanUtils.toBoolean(isIncludeRichText); } String temp = arg0.getInitParameter("excludes"); if (temp != null) { String[] url = temp.split(","); for (int i = 0; url != null && i < url.length; i++) { excludes.add(url[i]); } } } @Override public void destroy() { } }

3.5filter的配置类:XssConfig

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import com.xxx.filter.XssFilter; import com.google.common.collect.Maps; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.Map; /** * @Auther: qianshanmuxue * @Date: 2019/2/27 16:49 * @Description: xss filter config */ @Configuration public class XssConfig { @Bean public FilterRegistrationBean xssFilterRegistrationBean() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(new XssFilter()); filterRegistrationBean.setOrder(1);//filter order ,set it first filterRegistrationBean.setEnabled(true); filterRegistrationBean.addUrlPatterns("/*"); //set filter all url mapping Map<String, String> initParameters = Maps.newHashMap(); initParameters.put("excludes", "/oauth/token");///white list url initParameters.put("isIncludeRichText", "true");//enable or disable filterRegistrationBean.setInitParameters(initParameters); return filterRegistrationBean; } }

调试截图:

请求:

程序截图:

运行结果:

可以看到body中 的脚本已经被过滤了,

然后其他的截图我就不发了,还有一种思路就是在过滤器中把字符转义。

感谢luckpet大佬的提示

1 BufferedReader 使用完需要关闭 ;

2 对于一些拿postman等工具的朋友,拼接json的话会有换行 这里result += clean(line); 需要改成: while((line = br.readLine()) != null){ if (line != null) { result += line; } }

使用jsoup防止XSS攻击

前阵子项目国测后,打开一个项目页面,莫名其妙弹出xss,搜了全局也没找到alert("xss"),问了一下项目经理,原来是国测做防注入的时候,在添加数据的时候做的,一脸懵逼。

查了一下资料,以前做项目的时候都没想到这个问题,如果保存一段script脚本,查数据的时候,这段脚本就会被执行,这东西后果挺严重啊,如果是在桌面外弹框,执行个挖矿脚本,这玩意不得了啊,厉害,长知识了。。。

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

以上为个人经验,希望能给大家一个参考,也希望大家多多支持靠谱客。

最后

以上就是犹豫鸡最近收集整理的关于springboot中使用过滤器,jsoup过滤XSS脚本详解的全部内容,更多相关springboot中使用过滤器内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(209)

评论列表共有 0 条评论

立即
投稿
返回
顶部