抵御跨站脚本攻击XSS
XSS
XSS 攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是 JavaScript,但实际上也可以包括 Java、 VBScript、ActiveX、 Flash 或者甚至是普通的 HTML。攻击成功后,攻击者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和 cookie 等各种内容。
解决方案:将用户的数据进行转义
Hutool
Hutool 是一个 Java 工具包类库,对文件、流、加密解密、转码、正则、线程、XML 等 JDK 方法进行封装,组成各种 Util 工具类
1<dependency>
2 <groupId>cn.hutool</groupId>
3 <artifactId>hutool-all</artifactId>
4 <version>5.4.0</version>
5 </dependency>
对 Http 请求中的数据做转义
- 设置过滤器
- 覆盖 Http 请求的方法
增强类(装饰设计模式)
继承 HttpServletRequestWrapper 装饰类,装饰器封装了厂商的 Request 实现类。
覆盖 Wrapper 类的方法,对数据做转义。
soulboy/emos/wx/config/xss/XssHttpServletRequestWrapper.java
1package soulboy.emos.wx.config.xss;
2
3import cn.hutool.core.util.StrUtil;
4import cn.hutool.http.HtmlUtil;
5import cn.hutool.json.JSONUtil;
6
7import javax.servlet.ReadListener;
8import javax.servlet.ServletInputStream;
9import javax.servlet.http.HttpServletRequest;
10import javax.servlet.http.HttpServletRequestWrapper;
11import java.io.*;
12import java.nio.charset.Charset;
13import java.util.LinkedHashMap;
14import java.util.Map;
15
16public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
17 public XssHttpServletRequestWrapper(HttpServletRequest request) {
18 super(request);
19 }
20
21 /**
22 * 转义 getParameter()
23 */
24 @Override
25 public String getParameter(String name) {
26 String value= super.getParameter(name);
27 if(!StrUtil.hasEmpty(value)){
28 //使用 hutool.http.HtmlUtil 进行转义
29 value=HtmlUtil.filter(value);
30 }
31 return value;
32 }
33
34 /**
35 * 转义 getParameterValues(String name)
36 */
37 @Override
38 public String[] getParameterValues(String name) {
39 String[] values= super.getParameterValues(name);
40 if(values!=null){
41 for (int i=0;i<values.length;i++){
42 String value=values[i];
43 if(!StrUtil.hasEmpty(value)){
44 value=HtmlUtil.filter(value);
45 }
46 values[i]=value;
47 }
48 }
49 return values;
50 }
51
52 /**
53 * 转义 getParameterMap()
54 */
55 @Override
56 public Map<String, String[]> getParameterMap() {
57 Map<String, String[]> parameters = super.getParameterMap();
58 LinkedHashMap<String, String[]> map=new LinkedHashMap();
59 if(parameters!=null){
60 for (String key:parameters.keySet()){
61 String[] values=parameters.get(key);
62 for (int i = 0; i < values.length; i++) {
63 String value = values[i];
64 if (!StrUtil.hasEmpty(value)) {
65 //使用 hutool.http.HtmlUtil 进行转义
66 value = HtmlUtil.filter(value);
67 }
68 values[i] = value;
69 }
70 map.put(key,values);
71 }
72 }
73 return map;
74 }
75
76 /**
77 * 转义 getHeader(String name)
78 */
79 @Override
80 public String getHeader(String name) {
81 String value= super.getHeader(name);
82 if (!StrUtil.hasEmpty(value)) {
83 //使用 hutool.http.HtmlUtil 进行转义
84 value = HtmlUtil.filter(value);
85 }
86 return value;
87 }
88
89 /**
90 * 转义 getInputStream()
91 */
92 @Override
93 public ServletInputStream getInputStream() throws IOException {
94 InputStream in= super.getInputStream();
95 InputStreamReader reader=new InputStreamReader(in, Charset.forName("UTF-8"));
96 BufferedReader buffer=new BufferedReader(reader);
97 StringBuffer body=new StringBuffer();
98 String line=buffer.readLine();
99 while(line!=null){
100 body.append(line);
101 line=buffer.readLine();
102 }
103 buffer.close();
104 reader.close();
105 in.close();
106 Map<String,Object> map=JSONUtil.parseObj(body.toString());
107 Map<String,Object> result=new LinkedHashMap<>();
108 for(String key:map.keySet()){
109 Object val=map.get(key);
110 if(val instanceof String){
111 if(!StrUtil.hasEmpty(val.toString())){
112 //使用 hutool.http.HtmlUtil 进行转义
113 result.put(key,HtmlUtil.filter(val.toString()));
114 }
115 }
116 else {
117 result.put(key,val);
118 }
119 }
120 String json=JSONUtil.toJsonStr(result);
121 ByteArrayInputStream bain=new ByteArrayInputStream(json.getBytes());
122 return new ServletInputStream() {
123 @Override
124 public int read() throws IOException {
125 return bain.read();
126 }
127
128 @Override
129 public boolean isFinished() {
130 return false;
131 }
132
133 @Override
134 public boolean isReady() {
135 return false;
136 }
137
138 @Override
139 public void setReadListener(ReadListener readListener) {
140
141 }
142 };
143 }
144}
过滤器
注册过滤器,所有请求。
将 Request 对象传入 Wrapper 的实现类中
实现对用户请求数据的转义。
soulboy/emos/wx/config/xss/XssFilter.java
1package soulboy.emos.wx.config.xss;
2
3import javax.servlet.*;
4import javax.servlet.annotation.WebFilter;
5import javax.servlet.http.HttpServletRequest;
6import java.io.IOException;
7@WebFilter(urlPatterns = "/*")
8public class XssFilter implements Filter {
9 @Override
10 public void init(FilterConfig filterConfig) throws ServletException {
11
12 }
13
14 @Override
15 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
16 HttpServletRequest request= (HttpServletRequest) servletRequest;
17 XssHttpServletRequestWrapper wrapper=new XssHttpServletRequestWrapper(request);
18 filterChain.doFilter(wrapper,servletResponse);
19 }
20
21 @Override
22 public void destroy() {
23
24 }
25}
启动类添加注解
@ServletComponentScan 可以扫描过滤器的 WebFilter 注解
1package soulboy.emos.wx;
2
3import org.springframework.boot.SpringApplication;
4import org.springframework.boot.autoconfigure.SpringBootApplication;
5import org.springframework.boot.web.servlet.ServletComponentScan;
6
7@SpringBootApplication
8@ServletComponentScan //扫描 XssFilter 过滤器的 WebFilter注解
9public class EmosWxApiApplication {
10
11 public static void main(String[] args) {
12 SpringApplication.run(EmosWxApiApplication.class, args);
13 }
14
15}