springboot Xss(跨站攻击)
springboot Xss(跨站脚本攻击)
跨站脚本攻击(Cross Site Scripting),为了不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。
#依赖
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.13.1</version>
</dependency>
<!-- BooleanUtils在该依赖下 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.10</version>
</dependency>#工具类
?
/**
* xss过滤工具
*/
public class JsoupUtils {
//设置白名单
private static final Whitelist WHITELIST = Whitelist.basicWithImages();
//配置过滤参数不对代码格式化
private static final Document
.OutputSettings OUTPUT_SETTINGS = new Document
//默认开启,关闭输入的代码格式化
.OutputSettings().prettyPrint(false);
?
static {
//为标签添加属性,使用伪标签(:all表示所有标签),这里指白名单中的标签
//允许富文本编辑器设置行内样式
WHITELIST.addAttributes(":all", "style");
}
?
/**
* content是用户输入的内容,没有baseUri,所以设置空
* 过滤,如果不需要baseUri 就使用空字符串
* 从不信任的html片段中截取信任的片段
*/
public static String clean(String content) {
return Jsoup.clean(content, "", WHITELIST, OUTPUT_SETTINGS);
}
/*
这里能发现事件被过滤了
public static void main(String[] args) {
String text = "<a href=\"http://www.baidu.com/a\" onclick=\"alert(1);\">sss</a><script>alert(0);</script>sss";
System.out.println(clean(text));
}
*/
}?
#request包装类
?
/**
* 核心
* 过滤http请求中参数包含的恶意字符
* 需要重写getParameter,getParameterValues,getHeader
*/
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
//原始的请求
public HttpServletRequest orgRequest;
//是否包含富文本
private boolean isIncludeRichText;
?
?
public XssHttpServletRequestWrapper(HttpServletRequest request, boolean isIncludeRichText) {
super(request);
orgRequest = request;
this.isIncludeRichText = isIncludeRichText;
}
?
public boolean isIncludeRichText() {
return isIncludeRichText;
}
?
public void setIncludeRichText(boolean includeRichText) {
isIncludeRichText = includeRichText;
}
?
/**
* 过滤请求头
*/
@Override
public String getHeader(String name) {
JsoupUtils.clean(name);
String header = super.getHeader(name);
if (!StringUtils.isEmpty(header)) {
return JsoupUtils.clean(name);
}
return header;
}
?
/**
* 过滤请求的参数和值
* 覆盖getParameter方法,将参数名和参数值都做xss过滤。
* 如果需要获得原始的值,则通过super.getParameterValues(name)来获取
* getParameterNames,getParameterValues和getParameterMap也可能需要覆盖
*/
@Override
public String getParameter(String name) {
boolean condition = Objects.equals("content", name) || name.endsWith("WithHtml");
//如果请求的参数为content或是以WithHtml结尾的,且不包含富文本
if (condition && !isIncludeRichText) {
//不过滤参数
return super.getParameter(name);
}
//过滤参数
JsoupUtils.clean(name);
String value = super.getParameter(name);
//如果值不为null和空字符串""( " "不算空字符串因为就是判断长度)过滤值
if (!StringUtils.isEmpty(value)) {
JsoupUtils.clean(value);
}
return value;
}
?
/**
* 过滤单个参数多个值
* 如复选框
*/
@Override
public String[] getParameterValues(String name) {
String[] values = super.getParameterValues(name);
for (int i = 0; i < values.length; i++) {
//过滤值后重新赋值
values[i] = JsoupUtils.clean(values[i]);
}
return values;
}
?
public HttpServletRequest getOrgRequest() {
return orgRequest;
}
?
public void setOrgRequest(HttpServletRequest orgRequest) {
this.orgRequest = orgRequest;
}
?
/**
* 获取原始的request请求
*/
public static HttpServletRequest getOrgRequest(HttpServletRequest request) {
if (request instanceof XssHttpServletRequestWrapper) {
return ((XssHttpServletRequestWrapper) request).getOrgRequest();
}
return request;
}
}
?#filter
?
/**
* XssFilter过滤Xss请求的入口
* 拦截防止xss
*/
@Slf4j
public class XssFilter implements Filter {
//LoggerFactory log = LoggerFactory.getLogger(XssFilter.class)
//是否包含富文本内容
public static boolean IS_INCLUDE_RICH_TEXT = false;
public List<String> excludes = new ArrayList<>();
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.debug("----------------- xss filter init ----------------");
//获取filter中的初始参数
String isRichText = filterConfig.getInitParameter("isIncludeRichText");
if (!StringUtils.isEmpty(isRichText)) {
//将字符串转为布尔
IS_INCLUDE_RICH_TEXT = BooleanUtils.toBoolean(isRichText);
}
String temp = filterConfig.getInitParameter("excludes");
if (!StringUtils.isEmpty(temp)) {
String[] url = temp.split(",");
//spring工具类
Assert.notNull(url, "exclude不能为null");
excludes.addAll(Arrays.asList(url));
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
log.debug("----------------------xss filter is open----------------------");
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
if (handleExcludeURL(req, resp)) {
//包含exclude的url片段放行
chain.doFilter(request, response);
return;
}
//不包含exclude的url片段
//将request包装到XssHttpServletRequestWrapper
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(
(HttpServletRequest) request, IS_INCLUDE_RICH_TEXT);
//放行,交给spring处理
chain.doFilter(xssRequest, response);
}
private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) {
//spring工具类,判断集合是否为空集合或集合为null
if (CollectionUtils.isEmpty(excludes)) {
return false;
}
//获取请求的servletPath,不带协议+ip+端口+项目名
String servletPath = request.getServletPath();
for (String pattern : excludes) {
//这里的^表示匹配开头
Pattern p = Pattern.compile("^" + pattern);
//String实现CharSequence, 用pattern去匹配servlet
Matcher matcher = p.matcher(servletPath);
//判断请求的servletPath中是否有匹配pattern的,只要有一个就返回true
if (matcher.find()) {
return true;
}
}
return false;
}
}#配置类
?
@Configuration
public class JsoupConf {
/**
* 注册jsoup Filter
*/
@Bean
public FilterRegistrationBean<XssFilter> xssfilterRegistrationBean() {
FilterRegistrationBean<XssFilter> filterRegistrationBean =
new FilterRegistrationBean<>(new XssFilter());
//filterRegistrationBean.setFilter(new XssFilter());
//设置在filter中执行的顺序,为第一执行
filterRegistrationBean.setOrder(1);
//指明filter是否开启,默认开启
filterRegistrationBean.setEnabled(true);
//拦截所有请求
filterRegistrationBean.addUrlPatterns("/*");
HashMap<String, String> initParmas = new HashMap<>();
//路径自行替换为static下的,首页
initParmas.put("excludes", "/favicon.ico,/img/*,/js/*,/css/*,/index");
initParmas.put("isIncludeRichText", "true");
//设置filter的init-param
filterRegistrationBean.setInitParameters(initParmas);
return filterRegistrationBean;
}
}参考:https://www.jianshu.com/p/3e4b00b8ff3a?https://www.open-open.com/jsoup/