XSS是什么?如何防御?手摸手教你Springboot配置XSS防御,深入代码解析!


一、简单了解一下XSS

来,百度先抄一段。
HTML是一种超文本标记语言,通过将一些字符特殊地对待来区别文本和标记,例如,小于符号(<)被看作是HTML标签的开始,之间的字符是页面的标题等等。当动态页面中插入的内容含有这些特殊字符(如<)时,用户浏览器会将其误认为是插入了HTML标签,当这些HTML标签引入了一段JavaScript脚本时,这些脚本程序就将会在用户浏览器中执行。所以,当这些特殊字符不能被动态页面检查或检查出现失误时,就将会产生XSS漏洞。
再用一个通俗易懂的例子介绍一下
在没有使用防御之前,假设有个用户管理和用户注册的功能。前台注册用户,用户名中含有,那么在用户管理渲染这条用户信息时就会一直弹框。这就是一个简单的XSS攻击。

二、如何防御

传统的XSS防御在进行攻击鉴别时多采用特征匹配方式,主要是针对“javascript”这个关键字进行检索,但是这种鉴别不够灵活,凡是提交的信息中各有“javascript”时,就被硬性的被判定为XSS攻击。

基于代码修改的防御。Web页面开发者在编写程序时往往会出现一些失误和漏洞,XSS攻击正是利用了失误和漏洞,因此一种比较理想的方法就是通过优化Web应用开发来减少漏洞,避免被攻击:1)用户向服务器上提交的信息要对URL和附带的的HTTP头、POST数据等进行查询,对不是规定格式、长度的内容进行过滤。2)实现Session标记(session tokens)、CAPTCHA系统或者HTTP引用头检查,以防功能被第三方网站所执行。3)确认接收的的内容被妥善的规范化,仅包含最小的、安全的Tag(没有javascript),去掉任何对远程内容的引用(尤其是样式表和javascript),使用HTTP only的cookie。。

客户端分层防御策略。客户端跨站脚本攻击的分层防御策略是基于独立分配线程和分层防御策略的安全模型。它建立在客户端(浏览器),这是它与其他模型最大的区别,之所以客户端安全性如此重要,客户端在接受服务器信息,选择性的执行相关内容。这样就可以使防御XSS攻击变得容易,该模型主要由三大部分组成:1)对每一个网页分配独立线程且分析资源消耗的“网页线程分析模块”;2)包含分层防御策略四个规则的用户输入分析模块;3)保存互联网上有关XSS恶意网站信息的XSS信息数据库

如果你心情不好,上面的可以不看
如果你心情好,上面的其实也没什么看的必要,因为都是百度来的

现在
谷歌提供了一套转义的标准。什么大于号,小于号,都给你转成其它表示形式。
比如这样子:这里有全套,我知道你们都喜欢看全套
在这里插入图片描述
那么原理就是利用这一套标准。将转义后的字符串存入数据库,前台解析时再通过这个规则呈现。
怎么转,在哪里转?做法还是蛮有意思,先把代码放出来,毕竟有的人急着用。代码放出来,我再解释解释,我相信没有人不懂,不懂就到我床上坐坐,我好好给你讲讲。

SpringBoot配置XSS防御

首先maven导入需要的依赖,有已经导入的就不用重复导入了,最新版可以自己去maven官网找。

<!--xss防御-->
<dependency>
  <groupId>org.jsoup</groupId>
  <artifactId>jsoup</artifactId>
  <version>1.10.2</version>
</dependency>
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
  <version>3.5</version>
</dependency>
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>fastjson</artifactId>
  <version>1.2.73</version>
</dependency>

接下来要做的就是写个过滤器拦截处理了

写一个XssFilter过滤所有请求

扫描二维码关注公众号,回复: 12086642 查看本文章
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
 * XSS过滤器
 * @author Jozz
 */
@WebFilter(filterName="xssFilter",urlPatterns="/*")
public class XssFilter implements Filter {
    
    
    @Override
    public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
    
    
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
    
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        String path = request.getServletPath();
        //由于我的@WebFilter注解配置的是urlPatterns="/*"(过滤所有请求),所以这里对不需要过滤的静态资源url,作忽略处理(大家可以依照具体需求配置)
        String[] exclusionsUrls = {
    
    ".js",".gif",".jpg",".png",".css",".ico"};
        for (String str : exclusionsUrls) {
    
    
            if (path.contains(str)) {
    
    
                filterChain.doFilter(servletRequest,servletResponse);
                return;
            }
        }
        filterChain.doFilter(new XssHttpServletRequestWrapper(request),servletResponse);
    }
    @Override
    public void destroy() {
    
    
    }
};

然后再来一个ServletRequest包装类,这个类可能出现的莫名其妙,等下再来解释。

import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringEscapeUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
/**
 * ServletRequest包装类,对request做XSS过滤处理
 * @author Jozz
 */
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
    
    
    public XssHttpServletRequestWrapper(HttpServletRequest request) {
    
    
        super(request);
    }

    @Override
    public String getHeader(String name) {
    
    
        return StringEscapeUtils.escapeHtml4(super.getHeader(name));
    }
    @Override
    public String getQueryString() {
    
    
        return StringEscapeUtils.escapeHtml4(super.getQueryString());
    }
    @Override
    public String getParameter(String name) {
    
    
        return StringEscapeUtils.escapeHtml4(super.getParameter(name));
    }
    @Override
    public String[] getParameterValues(String name) {
    
    
        String[] values = super.getParameterValues(name);
        if(values != null) {
    
    
            int length = values.length;
            String[] escapseValues = new String[length];
            for(int i = 0; i < length; i++){
    
    
                escapseValues[i] = StringEscapeUtils.escapeHtml4(values[i]);
            }
            return escapseValues;
        }
        return values;
    }
    @Override
    public ServletInputStream getInputStream() throws IOException {
    
    
        String str=getRequestBody(super.getInputStream());
        Map<String,Object> map= JSON.parseObject(str,Map.class);
        Map<String,Object> resultMap=new HashMap<>(map.size());
        for(String key:map.keySet()){
    
    
            Object val=map.get(key);
            if(map.get(key) instanceof String){
    
    
                resultMap.put(key,StringEscapeUtils.escapeHtml4(val.toString()));
            }else{
    
    
                resultMap.put(key,val);
            }
        }
        str=JSON.toJSONString(resultMap);
        final ByteArrayInputStream bais = new ByteArrayInputStream(str.getBytes());
        return new ServletInputStream() {
    
    
            @Override
            public int read() throws IOException {
    
    
                return bais.read();
            }
            @Override
            public boolean isFinished() {
    
    
                return false;
            }
            @Override
            public boolean isReady() {
    
    
                return false;
            }
            @Override
            public void setReadListener(ReadListener listener) {
    
    
            }
        };
    }
    private String getRequestBody(InputStream stream) {
    
    
        String line = "";
        StringBuilder body = new StringBuilder();
        int counter = 0;
        // 读取POST提交的数据内容
        BufferedReader reader = new BufferedReader(new InputStreamReader(stream, Charset.forName("UTF-8")));
        try {
    
    
            while ((line = reader.readLine()) != null) {
    
    
                body.append(line);
                counter++;
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
        return body.toString();
    }
}

最后在启动类上加上@ServletComponentScan注解,用于扫描过滤器。

@SpringBootApplication
@MapperScan("com.kid.mapper")
//@MapperScan("com.baomidou.mybatisplus.samples.quickstart.mapper")
@ServletComponentScan//扫描过滤器
@EnableCaching
public class Boot {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(Boot.class, args);
    }
}

那么我现在启动一下,展示以一下效果
在这里插入图片描述
大家可以看到我在账号输入了一些非法字符,我在后台打印我获取到的信息是这样的
在这里插入图片描述
那么其实,收到的不是这样的,只不过是被转成这样了。

代码解析

我相信大家也都知道,这个事情是因为经过过滤器,处理之后才成功防御的。那么过滤器到底做了什么,我们可以看到过滤器似乎什么都没做就流下了,只是放行了一些图片其它文件请求的url。
但是我们仔细看流下是,传入的request对象
filterChain.doFilter(new XssHttpServletRequestWrapper(request),servletResponse);
这里的request对象被包裹成另一个对象,穿上了一件衣服。
其实这种设计模式叫做装饰者模式,我在讲简易连接池的篇章也专门讲过这个模式。有兴趣也可以去了解一下。
简易连接池及装饰者模式

我们在FilterChain接口中可以看到,var1参数需要传入的是ServletRequest类型的对象,因此这里的对象只要是实现ServletRequest 接口的都可以。

public interface FilterChain {
    
    
    void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}

我们在查看源码也可以看到HttpServletRequest 是一个接口,并且继承了ServletRequest 接口

public interface HttpServletRequest extends HttpServletRequest 

因此这个地方只需要实现传入实现HttpServletRequest 接口的类或者HttpServletRequest 接口的类即可。
因为我们做过滤器中拿到的request对象它是HttpServletRequest 类型的,我们要把这个包进去,那我们就必须写一个实现HttpServletRequest 接口的类,将这个request包进来。

但是从上面的代码上看,我们继承的是HttpServletRequestWrapper 对象。其实这个HttpServletRequestWrapper 对象,它就是实现HttpServletRequest接口,并且实现所有接口,且每个方法都原封不动抄下来。

public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper 

以上的部分其实可知道可不知道,但我觉得作为一个程序员,这些还是有必要懂的

那么我们知道过滤器中只是去替换了HttpServletRequest 对象,很显然,我们要实现XSS防御,只需要在getParameter等获取字符串的这些方法中,去将原本获取到的字符串,拿到工具类去转换一下再返回给调用者。

  @Override
    public String getParameter(String name) {
    
    
        return StringEscapeUtils.escapeHtml4(super.getParameter(name));
    }

下面就是我简单画了的一张骚图,哈哈哈哈哈哈或哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈,蛮看吧。下一章见!
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_39150049/article/details/109379025