springBoot:集成jsoup解决安全漏洞之XSS注入攻击
跨站点脚本编制
- 风险:可能会窃取或操纵客户会话和 cookie,它们可能用于模仿合法用户,从而使黑客能够以该用户身份查看或变更用户记录以及执行事务。
- 原因:未对用户输入正确执行危险字符清理
- 固定值:查看危险字符注入的可能解决方案
一、依赖
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.11.3</version>
</dependency>
二、自定义过滤器
XSS过滤处理逻辑
package com.yolo.springboot.kaptcha.filter;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.safety.Whitelist;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
/**
* XSS过滤处理
*/
@Slf4j
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
public XssHttpServletRequestWrapper(HttpServletRequest request)
{
super(request);
}
/**
* 获取头部参数
* @param v 参数值
*/
@Override
public String getHeader(String v) {
String header = super.getHeader(v);
if (header == null || "".equals(header)) {
return header;
}
return Jsoup.clean(super.getHeader(v), Whitelist.relaxed());
}
/**
* 获取参数
* @param v 参数值
*/
@Override
public String getParameter(String v) {
String param = super.getParameter(v);
if (param == null || "".equals(param)) {
return param;
}
return Jsoup.clean(super.getParameter(v), Whitelist.relaxed());
}
/**
* 获取参数值
* @param v 参数值
*/
@Override
public String[] getParameterValues(String v) {
String[] values = super.getParameterValues(v);
if (values == null) {
return values;
}
int length = values.length;
String[] resultValues = new String[length];
for (int i = 0; i < length; i++) {
// 过滤特殊字符
resultValues[i] = Jsoup.clean(values[i], Whitelist.relaxed()).trim();
if (!(resultValues[i]).equals(values[i])) {
log.debug("XSS过滤器 => 过滤前:{} => 过滤后:{}", values[i], resultValues[i]);
}
}
return resultValues;
}
}
防止XSS攻击的过滤器
package com.yolo.springboot.kaptcha.filter;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @ClassName XssFilter
* @Description 防止XSS攻击的过滤器
* @Author hl
* @Date 2022/12/7 10:07
* @Version 1.0
*/
public class XssFilter implements Filter {
/**
* 排除链接
*/
public List<String> noFilterUrls = new ArrayList<>();
/**
* xss过滤开关
*/
public boolean enabled = true;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 从过滤器配置中获取initParams参数
String noFilterUrl = filterConfig.getInitParameter("noFilterUrl");
// 将排除的URL放入成员变量noFilterUrls中
if (StringUtils.isNotBlank(noFilterUrl)) {
noFilterUrls = new ArrayList<>(Arrays.asList(noFilterUrl.split(",")));
}
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
if (!enabled || handleExcludeURL(req)) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(req);
filterChain.doFilter(xssRequest, servletResponse);
}
/**
* 判断是否为忽略的URL
* @return true-忽略,false-过滤
*/
private boolean handleExcludeURL(HttpServletRequest request) {
if (noFilterUrls == null || noFilterUrls.isEmpty()) {
return false;
}
String url = request.getServletPath();
// return excludes.stream().map(pattern -> Pattern.compile("^" + pattern)).map(p -> p.matcher(url))
// .anyMatch(Matcher::find);
for (String pattern : noFilterUrls) {
Pattern p = Pattern.compile("^" + pattern);
Matcher m = p.matcher(url);
if (m.find()) {
return true;
}
}
return false;
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
注册过滤器
package com.yolo.springboot.kaptcha.config;
import com.yolo.springboot.kaptcha.filter.XssFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<?> xssFilterRegistration() {
FilterRegistrationBean<XssFilter> registration = new FilterRegistrationBean<>();
// 将过滤器配置到FilterRegistrationBean对象中
registration.setFilter(new XssFilter());
// 给过滤器取名
registration.setName("xssFilter");
// 设置过滤器优先级,该值越小越优先被执行
registration.setOrder(0);
List<String> urlPatterns = new ArrayList<>();
urlPatterns.add("/*");
//这里需要填写排除上传文件的接口
Map<String, String> paramMap = new HashMap<>();
paramMap.put("noFilterUrl", "/login,/logout,/images/*");
// 设置initParams参数
registration.setInitParameters(paramMap);
// 设置urlPatterns参数
registration.setUrlPatterns(urlPatterns);
return registration;
}
}