一、什么是XSS漏洞
XSS攻击全称跨站脚本攻击,是为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS,XSS是一种在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。
二、如何避免XSS攻击
在SpringBoot框架里,可以通过新建过滤器的方式过滤所有的请求参数,这样代码改动范围比较小且对业务模块的侵入性比较低。
环境和框架
- 开发语言:Java
- 框架: SpringBoot:2.1.17.RELEASE
- 依赖组件: hutool-all:5.0.3
过滤html标记
1、创建XssHttpServletRequestWrapper
@Slf4j
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public String getHeader(String name) {
String value = super.getHeader(name);
if (StringUtils.isEmpty(value)) {
return value;
}
// 过滤html标记
return HtmlUtil.filter(value);
}
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
if (StringUtils.isEmpty(value)) {
return value;
}
// 过滤html标记
return HtmlUtil.filter(value);
}
@Override
public String[] getParameterValues(String name) {
String[] values = super.getParameterValues(name);
if(values == null) {
return null;
}
for (int i = 0; i < values.length; i++) {
if (StringUtils.isEmpty(values[i])) {
values[i] = values[i];
} else {
// 过滤html标记
values[i] = HtmlUtil.filter(values[i]);
}
}
return values;
}
@Override
public ServletInputStream getInputStream() throws IOException {
// 请求方法为POST时会触发这个方法
return getInputStreamWithXSSFilter();
}
public ServletInputStream getInputStreamWithXSSFilter() throws IOException {
// 从inputStream读取字符串
InputStream in = super.getInputStream();
StringBuffer body = new StringBuffer();
InputStreamReader reader = new InputStreamReader(in, Charset.forName("UTF-8"));
BufferedReader buffer = new BufferedReader(reader);
String line = buffer.readLine();
while (line != null) {
body.append(line);
line = buffer.readLine();
}
buffer.close();
reader.close();
in.close();
// 使用hutool的Html工具类过滤
String str = HtmlUtil.filter(body.toString());
// 双引号不需要过滤因此替换回原来的符号
str = str.replace(""","\"");
// 因为request里面的inputStream已经读取过,指针指向结尾重新读取会发生异常,
// 而且过滤后的字符串可能和之前的字符串不同,因此将过滤后的字符串重新转成inputStream返回
final ByteArrayInputStream bain = new ByteArrayInputStream(str.getBytes());
return new ServletInputStream() {
@Override
public int read() throws IOException {
return bain.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener listener) {
}
};
}
}
2、创建过滤器XssFilter
@WebFilter(urlPatterns = "/*", filterName = "xssFilter")
public class XssFilter implements Filter {
private 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、创建XSSFilterFilterConfig配置过滤器
@Configuration
public class XSSFilterConfig {
/**
* 注册xxs过滤器
* @return
*/
@Bean
public FilterRegistrationBean xxsFilterRegistration(){
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new XssFilter());
// 设置过滤器url匹配规则,也可以在 Filter 的实现类的 doFilter 里自定义更复杂路由匹配规则
filterRegistrationBean.addUrlPatterns("/*");
// 设置过滤器名称
filterRegistrationBean.setName("XssFilter");
// 设置过滤器优先级
filterRegistrationBean.setOrder(99);
return filterRegistrationBean;
}
}
XSSFilterFilterConfig的作用是通过@Bean注解的方式注册过滤器。
SpringBoot使用的是 ApplicationFilterChain 这个类管理和链式按顺序执行过滤器
public final class ApplicationFilterChain implements FilterChain {
...
添加的过滤器的实例都保存在 ApplicationFilterChain 的 filters 里面,每当有新的请求进入就会依次经过 filters 里的过滤器
主要代码来源于网络,主要是对 getInputStream 方法里存在的问题尝试进行修改
参考: https://blog.csdn.net/Sunshine_zjh/article/details/118054764
git仓库: https://gitee.com/lai-xuxiang/csrf-xss-demo