SpringBoot 解决跨站脚本漏洞(XSS)问题
解决方案
步骤如下:
1、添加maven依赖
在 pom.xml 文件中,增加如下依赖:
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
<version>8.0.36</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
2、新增 XSSFilter.java
在 config 目录下增加 XSSFilter.java,用于对请求接口进行过滤。代码如下:
import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import java.io.IOException; @WebFilter(filterName="XSSFilter", urlPatterns="/*") public class XSSFilter implements Filter { FilterConfig filterConfig = null; @Override public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { filterChain.doFilter(new XssHttpServletRequestWrapper((HttpServletRequest) servletRequest), servletResponse); } @Override public void destroy() { this.filterConfig = null; } }
3、新增 XssHttpServletRequestWrapper.java
在config目录下,新增 XssHttpServletRequestWrapper.java,用于对请求参数进行预处理和解析。代码如下:
import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.Charset; import java.util.HashMap; import java.util.Map; public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { public XssHttpServletRequestWrapper(HttpServletRequest request) { super(request); } @Override public String[] getParameterValues(String parameter) { String[] values = super.getParameterValues(parameter); if (values==null) { return null; } int count = values.length; String[] encodedValues = new String[count]; for (int i = 0; i < count; i++) { encodedValues[i] = cleanXSS(values[i]); } return encodedValues; } @Override public String getParameter(String parameter) { String value = super.getParameter(parameter); if (value != null) { return cleanXSS(value); } return null; } /** * 对 application/x-www-form-urlencoded 格式的POST请求参数,进行 cleanXSS解析 * @return cleanXSS解析后的参数 */ @Override public Map<String, String[]> getParameterMap() { Map<String, String[]> values = super.getParameterMap(); if (values == null) { return null; } Map<String, String[]> result = new HashMap<>(); for (String key : values.keySet()) { String encodedKey = cleanXSS(key); int count = values.get(key).length; String[] encodedValues = new String[count]; for (int i = 0; i < count; i++) { encodedValues[i] = cleanXSS(values.get(key)[i]); } result.put(encodedKey, encodedValues); } return result; } @Override public String getHeader(String name) { String value = super.getHeader(name); if (value == null) return null; return cleanXSS(value); } private static String cleanXSS(String value) { value = value.replaceAll("<", "<").replaceAll(">", ">"); value = value.replaceAll("%3C", "<").replaceAll("%3E", ">"); value = value.replaceAll("\\(", "(").replaceAll("\\)", ")"); value = value.replaceAll("%28", "(").replaceAll("%29", ")"); value = value.replaceAll("'", "'"); value = value.replaceAll("eval\\((.*)\\)", ""); value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\""); value = value.replaceAll("script", ""); return value; } @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream bais = new ByteArrayInputStream(inputHandlers(super.getInputStream ()).getBytes ()); return new ServletInputStream() { @Override public int read() throws IOException { return bais.read(); } @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } }; } public String inputHandlers(ServletInputStream servletInputStream){ StringBuilder sb = new StringBuilder(); BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader(servletInputStream, Charset.forName("UTF-8"))); String line = ""; while ((line = reader.readLine()) != null) { sb.append(line); } } catch (IOException e) { e.printStackTrace(); } finally { if (servletInputStream != null) { try { servletInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } return cleanXSS(sb.toString ()); } }
4、添加注解 @ServletComponentScan
在 XXXApplication.java 的类名上方,用于扫描配置类。添加注解如下:
@ServletComponentScan("com.XXX.config")
示例如下: