使用过滤器解决前端XSS安全问题,JS注入问题
使用过滤器解决前端 XSS 安全问题
在Java web 项目中,对于前端 JS 注入问题,可以在前端写 JS 代码进行预处理,但是这样处理无法保证万无一失,当用户篡改前端校验的 JS 代码,导致校验失效,那么未经处理的 JS 代码保存到数据库后再次查询展示到页面,依然会出现 JS 安全问题,这时候就需要在后台做 JS 代码校验和处理。我们可以使用过滤器(Filter)处理。
过滤器
过滤器可以对目标 web 资源的请求和响应进行拦截,并做一些特定处理。
在spring boot中,针对 JS 注入问题,我们可以创建一个拦截器,文件名:XssJsonFilter
package com.qtong.afin.module.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @author duchong
* @version 1.0
* @date 2020/9/23 12:16
*/
@WebFilter(filterName="XssJsonFilter",urlPatterns={
"/workOrder/*","/Product/addProduct","/ProType/addProType/*"})
@Slf4j
public class XssJsonFilter 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;
}
}
urlPatterns
后面是需要拦截的路径,有以下几种形式:
- 以指定资源匹配,多资源用
,
隔开。 例如:"/Product/addProduct","/index.jsp"。- 以目录匹配,多资源用
,
隔开。 例如:/Product/
- 以后缀名匹配,多资源用
,
隔开。例如:*.jsp
- 通配符,拦截所有资源。
/*
这里有一个点我们需要注意: 在拦截器 XssJsonFilter
中,我们并没有使用 @Component
注解, 因为该注解会导致拦截器拦截所有的请求,这并不符合我们的预期,在实际开发中,我们可能只需要拦截部分请求,比如对于用户的查询请求、数据保存请求等,我们对于这些特定请求做统一处理即可,所以使用 @WebFilter
注解,在该注解上声明拦截路径。增加 @Component
注解后,会导致自定义的拦截路径失效,从而导致拦截用户的所有请求。
为什么使用 @Component
注解后会出现这种情况?
这是由于Spring的配置加载顺序导致的,Spring 会优先扫描加载
@Webfilter
注解,再加载@Componet
注解 ,由于@Componet
默认是全路径,所以会覆盖@Webfilter
注解的匹配路径。
还需要两个类,用于拦截后的逻辑处理,分别是 XssAndSqlHttpServletRequestWrapper
和 XssStringJsonSerializer
package com.qtong.afin.module.filter;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
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;
}
}
package com.qtong.afin.module.filter;
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;
/**
* @author duchong
* @version 1.0
* @date 2020/9/24 11:08
*/
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);
}
}
}
注意:
上述过滤器中,实际上是使用
StringEscapeUtils.escapeHtml4(value);
方法对一些常见的攻击性JS代码进行了转义,在使用过滤器后,前端页面就无需再写预处理的 JS 校验代码,否则可能会出现二次转义导致数据错误。