Skip to content

Commit 3e78735

Browse files
committed
feat: 提供[预防XSS攻击]功能
1 parent 47b5e80 commit 3e78735

File tree

6 files changed

+488
-0
lines changed

6 files changed

+488
-0
lines changed

mpdemo/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@
4444
<artifactId>lombok</artifactId>
4545
<optional>true</optional>
4646
</dependency>
47+
<!--jsoup-->
48+
<dependency>
49+
<groupId>org.jsoup</groupId>
50+
<artifactId>jsoup</artifactId>
51+
<version>1.13.1</version>
52+
</dependency>
4753
<dependency>
4854
<groupId>cn.hutool</groupId>
4955
<artifactId>hutool-all</artifactId>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.jiangfeixiang.mpdemo.springbootmp.controller;
2+
3+
import org.springframework.web.bind.annotation.GetMapping;
4+
import org.springframework.web.bind.annotation.RequestMapping;
5+
import org.springframework.web.bind.annotation.RequestParam;
6+
import org.springframework.web.bind.annotation.RestController;
7+
8+
/**
9+
* @author zl
10+
* @date 2022-07-04 17:53
11+
*/
12+
@RestController
13+
@RequestMapping(value = "/test")
14+
public class DemoTestController {
15+
16+
/**
17+
* 测试 预防XSS攻击效果演示
18+
* <pre>
19+
* http://localhost:8889/test/xss?htmlStr=<a href="www.baidu.com" onclick="alert(1);">baidu.com</a><script>alert(1);</script>
20+
* http://localhost:8889/test/xss?htmlStr=<img width="200px" onerr="alert(1);"/>
21+
* </pre>
22+
*
23+
* @param htmlStr 带有脚本的字符串内容
24+
* @return 剔除脚本的字符
25+
*/
26+
@GetMapping(value = "/xss")
27+
public String testXss(@RequestParam(value = "htmlStr") String htmlStr) {
28+
System.out.println("xss-html: \n" + htmlStr);
29+
return htmlStr;
30+
}
31+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package com.jiangfeixiang.mpdemo.springbootmp.util.xss;
2+
3+
import org.jsoup.Jsoup;
4+
import org.jsoup.nodes.Document;
5+
import org.jsoup.safety.Whitelist;
6+
7+
import java.io.IOException;
8+
import java.util.List;
9+
10+
/**
11+
* HTML 元素过滤器
12+
*
13+
* @author zl
14+
*/
15+
public class HtmlFilter {
16+
17+
/**
18+
* 默认使用relaxed()
19+
* 允许的标签: a, b, blockquote, br, caption, cite, code, col, colgroup, dd, dl, dt, em, h1, h2, h3, h4, h5, h6, i, img, li, ol, p, pre, q, small, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, u, ul。
20+
* 结果不包含标签rel=nofollow ,如果需要可以手动添加。
21+
*/
22+
private Whitelist whiteList;
23+
24+
25+
/**
26+
* 配置过滤化参数,不对代码进行格式化
27+
*/
28+
private Document.OutputSettings outputSettings;
29+
30+
31+
private HtmlFilter() {
32+
}
33+
34+
/**
35+
* 静态创建HtmlFilter方法
36+
*
37+
* @param whiteList 白名单标签
38+
* @param pretty 是否格式化
39+
* @return HtmlFilter
40+
*/
41+
public static HtmlFilter create(Whitelist whiteList, boolean pretty) {
42+
HtmlFilter filter = new HtmlFilter();
43+
if (whiteList == null) {
44+
filter.whiteList = Whitelist.relaxed();
45+
}
46+
filter.outputSettings = new Document.OutputSettings().prettyPrint(pretty);
47+
return filter;
48+
}
49+
50+
/**
51+
* 静态创建HtmlFilter方法
52+
*
53+
* @return HtmlFilter
54+
*/
55+
public static HtmlFilter create() {
56+
return create(null, false);
57+
}
58+
59+
/**
60+
* 静态创建HtmlFilter方法
61+
*
62+
* @param whiteList 白名单标签
63+
* @return HtmlFilter
64+
*/
65+
public static HtmlFilter create(Whitelist whiteList) {
66+
return create(whiteList, false);
67+
}
68+
69+
/**
70+
* 静态创建HtmlFilter方法
71+
*
72+
* @param excludeTags 例外的特定标签
73+
* @param includeTags 需要过滤的特定标签
74+
* @param pretty 是否格式化
75+
* @return HtmlFilter
76+
*/
77+
public static HtmlFilter create(List<String> excludeTags, List<String> includeTags, boolean pretty) {
78+
HtmlFilter filter = create(null, pretty);
79+
//要过滤的标签
80+
if (includeTags != null && !includeTags.isEmpty()) {
81+
String[] tags = (String[]) includeTags.toArray(new String[0]);
82+
filter.whiteList.removeTags(tags);
83+
}
84+
//例外标签
85+
if (excludeTags != null && !excludeTags.isEmpty()) {
86+
String[] tags = (String[]) excludeTags.toArray(new String[0]);
87+
filter.whiteList.addTags(tags);
88+
}
89+
return filter;
90+
}
91+
92+
93+
/**
94+
* 静态创建HtmlFilter方法
95+
*
96+
* @param excludeTags 例外的特定标签
97+
* @param includeTags 需要过滤的特定标签
98+
* @return HtmlFilter
99+
*/
100+
public static HtmlFilter create(List<String> excludeTags, List<String> includeTags) {
101+
return create(includeTags, excludeTags, false);
102+
}
103+
104+
/**
105+
* @param content 需要过滤内容
106+
* @return 过滤后的String
107+
*/
108+
public String clean(String content) {
109+
return Jsoup.clean(content, "", this.whiteList, this.outputSettings);
110+
111+
}
112+
113+
// 测试:使用 Jsoup 剔除对应的脚本(即 jsoup的规则只允许指定的富文本标签通过)
114+
public static void main(String[] args) throws IOException {
115+
String text = "<a href=\"http://www.baidu.com/a\" onclick=\"alert(1);\"></a><script>alert(0);</script><b style=\"xxx\" onclick=\"<script>alert(0);</script>\">abc</>";
116+
System.out.println(HtmlFilter.create().clean(text));
117+
}
118+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package com.jiangfeixiang.mpdemo.springbootmp.util.xss;
2+
3+
import org.apache.commons.lang3.StringUtils;
4+
5+
import javax.servlet.*;
6+
import javax.servlet.http.HttpServletRequest;
7+
import javax.servlet.http.HttpServletResponse;
8+
import java.io.IOException;
9+
import java.util.ArrayList;
10+
import java.util.Collections;
11+
import java.util.List;
12+
import java.util.regex.Matcher;
13+
import java.util.regex.Pattern;
14+
15+
/**
16+
* XSS 过滤器
17+
*
18+
* @author zl
19+
*/
20+
public class XssFilter implements Filter {
21+
/**
22+
* 例外urls
23+
*/
24+
private List<String> excludeUrls = new ArrayList<>();
25+
26+
/**
27+
* 例外标签
28+
*/
29+
private List<String> excludeTags = new ArrayList<>();
30+
31+
/**
32+
* 需要过滤标签
33+
*/
34+
private List<String> includeTags = new ArrayList<>();
35+
36+
/**
37+
* 开关
38+
*/
39+
public boolean enabled = false;
40+
41+
/**
42+
* 编码
43+
*/
44+
private String encoding = "UTF-8";
45+
46+
@Override
47+
public void init(FilterConfig filterConfig) throws ServletException {
48+
String enabledStr = filterConfig.getInitParameter("enabled");
49+
String excludeUrlStr = filterConfig.getInitParameter("urlPatterns");
50+
String excludeTagStr = filterConfig.getInitParameter("excludes");
51+
String includeTagStr = filterConfig.getInitParameter("includes");
52+
String encodingStr = filterConfig.getInitParameter("encoding");
53+
54+
if (StringUtils.isNotEmpty(excludeUrlStr)) {
55+
String[] url = excludeUrlStr.split(",");
56+
Collections.addAll(this.excludeUrls, url);
57+
}
58+
59+
if (StringUtils.isNotEmpty(excludeTagStr)) {
60+
String[] url = excludeTagStr.split(",");
61+
Collections.addAll(this.excludeTags, url);
62+
}
63+
64+
if (StringUtils.isNotEmpty(includeTagStr)) {
65+
String[] url = includeTagStr.split(",");
66+
Collections.addAll(this.includeTags, url);
67+
}
68+
69+
if (StringUtils.isNotEmpty(enabledStr)) {
70+
this.enabled = Boolean.parseBoolean(enabledStr);
71+
}
72+
73+
if (StringUtils.isNotEmpty(encodingStr)) {
74+
this.encoding = encodingStr;
75+
}
76+
77+
}
78+
79+
@Override
80+
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
81+
HttpServletRequest req = (HttpServletRequest) request;
82+
HttpServletResponse resp = (HttpServletResponse) response;
83+
if (handleExcludeUrls(req, resp)) {
84+
chain.doFilter(request, response);
85+
return;
86+
}
87+
88+
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request, encoding, excludeTags, includeTags);
89+
chain.doFilter(xssRequest, response);
90+
91+
}
92+
93+
private boolean handleExcludeUrls(HttpServletRequest request, HttpServletResponse response) {
94+
if (!enabled) {
95+
return true;
96+
}
97+
if (excludeUrls == null || excludeUrls.isEmpty()) {
98+
return false;
99+
}
100+
String url = request.getServletPath();
101+
for (String pattern : excludeUrls) {
102+
Pattern p = Pattern.compile("^" + pattern);
103+
Matcher m = p.matcher(url);
104+
if (m.find()) {
105+
return true;
106+
}
107+
}
108+
return false;
109+
}
110+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.jiangfeixiang.mpdemo.springbootmp.util.xss;
2+
3+
import org.springframework.beans.factory.annotation.Value;
4+
import org.springframework.boot.web.servlet.FilterRegistrationBean;
5+
import org.springframework.context.annotation.Bean;
6+
import org.springframework.context.annotation.Configuration;
7+
8+
import javax.servlet.DispatcherType;
9+
import java.util.HashMap;
10+
import java.util.Map;
11+
12+
@Configuration
13+
public class XssFilterConfig {
14+
15+
@Value("${xss.enabled:true}")
16+
private String enabled;
17+
18+
@Value("${xss.excludes:}")
19+
private String excludes;
20+
21+
@Value("${xss.includes$:}")
22+
private String includes;
23+
24+
@Value("${xss.urlPatterns:/*}")
25+
private String urlPatterns;
26+
27+
@Bean
28+
public FilterRegistrationBean<XssFilter> xssFilterRegistrationBean() {
29+
FilterRegistrationBean<XssFilter> registration = new FilterRegistrationBean<>();
30+
registration.setDispatcherTypes(DispatcherType.REQUEST);
31+
registration.setFilter(new XssFilter());
32+
registration.addUrlPatterns(urlPatterns.split(","));
33+
registration.setName("XssFilter");
34+
registration.setOrder(Integer.MAX_VALUE);
35+
Map<String, String> initParameters = new HashMap<>(3);
36+
initParameters.put("excludes", excludes);
37+
initParameters.put("includes", includes);
38+
initParameters.put("enabled", enabled);
39+
registration.setInitParameters(initParameters);
40+
return registration;
41+
}
42+
}

0 commit comments

Comments
 (0)