【快学SpringBoot】过滤XSS脚本攻击(包括json格式)

8,306 阅读3分钟

XSS攻击是什么

XSS攻击全称跨站脚本攻击,是为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS,XSS是一种在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。

简而言之,就是作恶用户通过表单提交一些前端代码,如果不做处理的话,这些前端代码将会在展示的时候被浏览器执行。

如何避免XSS攻击

解决XSS攻击,可以通过后端对输入的数据做过滤或者转义,使XSS攻击代码失效。

代码实现

对于过滤XSS脚本的代码,通过搜索引擎可以搜索到很多,但似乎都不是那么全面。基本上都是只能过滤querystring(表单类型)类型的入参,而不能过滤json类型的入参。其实,在现在的开发中,更多的是使用json类型做数据交互。下面就直接贴代码了:

新建XssAndSqlHttpServletRequestWrapper.java

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
/**
 * @author Happy
 * 防止XSS攻击
 */
public class XssAndSqlHttpServletRequestWrapper extends HttpServletRequestWrapper {
 private HttpServletRequest request;
 public XssAndSqlHttpServletRequestWrapper(HttpServletRequest request) {
 super(request);
 this.request = request;
 }
 @Override
 public String getParameter(String name) {
 String value = request.getParameter(name);
 if (!StringUtils.isEmpty(value)) {
 value = StringEscapeUtils.escapeHtml4(value);
 }
 return value;
 }
 @Override
 public String[] getParameterValues(String name) {
 String[] parameterValues = super.getParameterValues(name);
 if (parameterValues == null) {
 return null;
 }
 for (int i = 0; i < parameterValues.length; i++) {
 String value = parameterValues[i];
 parameterValues[i] = StringEscapeUtils.escapeHtml4(value);
 }
 return parameterValues;
 }
}

这里重写了两个方法:getParameter和getParameterValues,getParameter方法是直接通过request获得querystring类型的入参调用的方法。如果是通过springMVC注解类型来获得参数的话,走的是getParameterValues的方法。大家可以通过打印一个输出来验证一下。

StringEscapeUtils.escapeHtml4这个方法来自Apache的工具类,maven坐标如下:

<dependency>
 <groupId>org.apache.commons</groupId>
 <artifactId>commons-text</artifactId>
 <version>1.4</version>
</dependency>

新建XssFilter.java

过滤的代码写完了,下面就是在一个filter中应用该代码。

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
 * @author Happy
 */
@WebFilter
@Component
public class XssFilter implements Filter {
 @Override
 public void init(FilterConfig filterConfig) throws ServletException {
 }
 @Override
 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
 throws IOException, ServletException {
 HttpServletRequest req = (HttpServletRequest) request;
 XssAndSqlHttpServletRequestWrapper xssRequestWrapper = new XssAndSqlHttpServletRequestWrapper(req);
 chain.doFilter(xssRequestWrapper, response);
 }
 @Override
 public void destroy() {
 }
 /**
 * 过滤json类型的
 * @param builder
 * @return
 */
 @Bean
 @Primary
 public ObjectMapper xssObjectMapper(Jackson2ObjectMapperBuilder builder) {
 //解析器
 ObjectMapper objectMapper = builder.createXmlMapper(false).build();
 //注册xss解析器
 SimpleModule xssModule = new SimpleModule("XssStringJsonSerializer");
 xssModule.addSerializer(new XssStringJsonSerializer());
 objectMapper.registerModule(xssModule);
 //返回
 return objectMapper;
 }
}

过滤表单类型的代码已经完成(xssObjectMapper这个是后面过滤json类型才用到的)。下面来实现过滤json类型的代码:

新建XssStringJsonSerializer.java

代码如下:

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.apache.commons.text.StringEscapeUtils;
import java.io.IOException;
public class XssStringJsonSerializer extends JsonSerializer<String> {
 @Override
 public Class<String> handledType() {
 return String.class;
 }
 @Override
 public void serialize(String value, JsonGenerator jsonGenerator,
 SerializerProvider serializerProvider) throws IOException {
 if (value != null) {
 String encodedValue = StringEscapeUtils.escapeHtml4(value);
 jsonGenerator.writeString(encodedValue);
 }
 }
}

这里是通过修改SpringMVC的json序列化来达到过滤xss的目的的。其实也可以通过第一种方法,重写getInputStream方法来实现,这里我就不做演示了(通过json类型传参会走getInputStream方法,通过重写该方法打印输出可以证明)。

测试

TestController.java

import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
/**
 * @author Happy
 */
@RestController
@RequestMapping(value = "/test")
public class TestController {
 @PostMapping(value = "/xss")
 public Object test(String name) {
 System.out.println(name);
 return name;
 }
 @PostMapping(value = "/json")
 public Object testJSON(@RequestBody Param param) {
 return param;
 }
 @GetMapping(value = "/query")
 public Object testQuery(String q){
 return q;
 }
 @PostMapping(value = "/upload")
 public Object upload(MultipartFile file){
 System.out.println(file.getOriginalFilename());
 return "OK";
 }
}

下面通过postman测试下效果:

【快学springboot】15、SpringBoot过滤XSS脚本攻击

【快学springboot】15、SpringBoot过滤XSS脚本攻击

【快学springboot】15、SpringBoot过滤XSS脚本攻击

可以看到,js代码已经经过转义。转义过后的代码,即使前端读取过去了,也不会被浏览器执行的。