抵御跨站脚本(XSS)攻击[亲测有效]

抵御跨站脚本(XSS)攻击[亲测有效]已经好久没有更新博客,更多的是因为自律的问题,要好好坚持学习,写一些学习的笔记;之前在学习的时候,总是没有注意到网站安全的方面,今天在看B站的时候无意间看到了XSS相关内容,今天就好好梳理一下XSS的相关知识,能够在以后的工作中避免XSS对网站造成的威胁,其实,一个网站能否安全…

已经好久没有更新博客,更多的是因为自律的问题,要好好坚持学习,写一些学习的笔记;之前在学习的时候,总是没有注意到网站安全的方面,今天在看B站的时候无意间看到了XSS相关内容,今天就好好梳理一下XSS的相关知识,能够在以后的工作中避免XSS对网站造成的威胁,其实,一个网站能否安全的运行,需要深入的学习安全知识。

一、什么是XSS?

百度百科的解释

通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java、 VBScript、ActiveX、 Flash 或者甚至是普通的HTML。攻击成功后,攻击者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。

我认为:

XSS就是恶意用户上传恶意脚本代码,访问用户私有的资源

我们在前后端的分离的项目当中,不管使用JWT,还是其他的技术,我们都喜欢将后端的Token保存在前端,如果存在XSS攻击,就会造成威胁,比如:将token存储在localstorage当中,当用户成功登录后,恶意程序将会调用localStorage.getItem获取用户token,并且通过ajax或者其他的方式将token发送给恶意用户,恶意用户就可以拿着这个token去访问受限的资源,获取隐私信息。

抵御跨站脚本(XSS)攻击[亲测有效]

以上为简单的一个XSS攻击的图,因为主要是想要简单的表达意思,可能一些表达和思路不够完善,我后面会进行补充

二、XSS攻击的危害

例如用户在发帖或者注册的时候,在文本框中输入**<script>alert('xss')</script>,这段代码如果不经过转义处理,而直接保存到数据库。将来视图层渲染HTML的时候,把这段代码输出到页面上,那么<script>**标签的内容就会被执行。

<html>
  //该脚本会直接进行运行
	<script>alert('xss')</script>
</html>

所以避免XSS攻击最有效的办法就是对用户输入的数据进行转义,然后存储到数据库里面。等到视图层渲染HTML页面的时候。转义后的文字是不会被当做JavaScript执行的,这就可以抵御XSS攻击。

1.上传恶意代码
<script>alert("xss")</script>

2.服务器进行转移后
alert("xss")

转义后将不会作为脚本处理,去除所有的标签,作为简单的字符串

三、解决XSS威胁

我们这里使用hutool工具类,里面提供了解决XSS威胁的方法

HtmlUtil.filter 过滤HTML文本,防止XSS攻击

String html = "<alert></alert>";
// 结果为:""
String filter = HtmlUtil.filter(html);

hutool.cn/

3.1 导入依赖

<!--hutool工具类-->
<dependency>
   <groupId>cn.hutool</groupId>
   <artifactId>hutool-all</artifactId>
   <version>5.4.0</version>
</dependency>

3.2 定义请求包装类

HttpServletRequest是一个接口,里面有许多的待实现的方法,如果我们实现该接口,是一个非常耗时的操作,是不现实的。

所以,我们选择继承**HttpServletRequestWrapper**

HttpServletRequestWrapper是用了装饰器模式,也相当于一个过滤器,如果需要获取参数或者返回值只需要继承该类重写某一个方法就可以实现,比如:我们需要对请求进行拦截,如果请求参数的值等于yangzinan,我们就将它转换为mic,我们只需要继承,重写其中的方法:

 @Override
    public String getParameter(String name) {
        //获取参数
        String value = super.getParameter(name);
        if(value == "yangzinan"){
            value = "mic";
        }
      	//将value返回,servlet获取的参数就是为mic
        return value;
    }

我们现在就正式开始编写代码:

/** * 防止xss攻击 */
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {

    /** * 构造器,将request传递给父类 * @param request */
    public XssHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
    }

    /** * 获取参数,将参数进行转义 * @param name * @return */
    @Override
    public String getParameter(String name) {
        //获取参数
        String value = super.getParameter(name);
        if(!StrUtil.hasBlank(value)){
            //对参数进行转义
            value = HtmlUtil.filter(value);
        }
        return value;
    }

    /** * 对参数进行转义,返回值为数组 * @param name * @return */
    @Override
    public String[] getParameterValues(String name) {
        //获取集合
        String[] values = super.getParameterValues(name);
        //判断集合是否为空,如果不为空,进行转义
        if(values != null){
            //遍历数组
            for(int i = 0;i < values.length;i++){
                String value = values[i];
                if(!StrUtil.hasBlank(value)){
                    //转义
                    value = HtmlUtil.filter(value);
                }
                //将转义后的数据放回数组
                values[i] = value;
            }
        }
        return values;
    }

    /** * 获取请求头的数据,并进行转义 * @param name * @return */
    @Override
    public String getHeader(String name) {
        String value =  super.getHeader(name);
        if(!StrUtil.hasBlank(value)){
            value = HtmlUtil.filter(value);
        }
        return value;
    }

    /** * 获取参数 * @return */
    @Override
    public Map<String, String[]> getParameterMap() {
        Map<String, String[]> parameterMap = super.getParameterMap();
        //因为super.getParameterMap()返回的是Map,所以我们需要定义Map的实现类对数据进行封装
        Map<String,String[]> params = new LinkedHashMap<>();
        //如果参数不为空
        if(parameterMap != null){
            //对map进行遍历
            for(String key:parameterMap.keySet()){
                //根据key获取value
                String[] values = parameterMap.get(key);
                //遍历数组
                for(int i = 0;i<values.length;i++){
                    String value = values[i];
                    if(!StrUtil.hasBlank(value)){
                        //转义
                        value = HtmlUtil.filter(value);
                    }
                    //将转义后的数据放回数组中
                    values[i] = value;
                }
                //将转义后的数组put到linkMap当中
                params.put(key,values);
            }
        }
        return params;
    }

    /** * 获取输入流 * @return * @throws IOException */
    @Override
    public ServletInputStream getInputStream() throws IOException {
        //获取输入流
        ServletInputStream in = super.getInputStream();
        //用于存储输入流
        StringBuffer body = new StringBuffer();
        InputStreamReader reader = new InputStreamReader(in, Charset.forName("UTF-8"));
        BufferedReader bufferedReader = new BufferedReader(reader);
        //按行读取输入流
        String line = bufferedReader.readLine();
        while(line != null){
            //将获取到的第一行数据append到StringBuffer中
            body.append(line);
            //继续读取下一行流,直到line为空
            line = bufferedReader.readLine();
        }
        //关闭流
        bufferedReader.close();
        reader.close();
        in.close();

        //将body转换为map
        Map<String,Object> map = JSONUtil.parseObj(body.toString());
        //创建空的map用于存储结果
        Map<String,Object> resultMap = new HashMap<>(map.size());
        //遍历数组
        for(String key:map.keySet()){
            Object value = map.get(key);
            //如果map.get(key)获取到的是字符串就需要进行转义,如果不是直接存储resultMap
            if(map.get(key) instanceof String){
                resultMap.put(key,HtmlUtil.filter(value.toString()));
            }else{
                resultMap.put(key,value);
            }
        }

        //将resultMap转换为json字符串
        String resultStr = JSONUtil.toJsonStr(resultMap);
        //将json字符串转换为字节
        final ByteArrayInputStream bis = new ByteArrayInputStream(resultStr.getBytes());

        //实现接口
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener listener) {

            }

            @Override
            public int read() throws IOException {
                return bis.read();
            }
        };
    }
}

3.3 创建过滤器

我们需要创建一个过滤器,将我们自定义的请求包装类注入过滤器,能够生效

/** * XSS过滤器 */
@WebFilter(urlPatterns = "/*")
public class XssFilter implements Filter {


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        XssHttpServletRequestWrapper xssHttpServletRequestWrapper = new XssHttpServletRequestWrapper((HttpServletRequest) request);
        chain.doFilter(xssHttpServletRequestWrapper,response);
    }

    @Override
    public void destroy() {

    }
}

其中**@WebFilter(urlPatterns = “/*”)**表示拦截所有请求,将所有的参数进行转义

3.4 测试

测试之前,我们需要增加**@ServletComponentScan**注解

@ServletComponentScan
@SpringBootApplication
public class GoflyWxApiApplication {

   public static void main(String[] args) {
      SpringApplication.run(GoflyWxApiApplication.class, args);
   }

}

测试controller

@Api("测试接口")
@RestController
public class TestController {

    @PostMapping("/test")
    public R testController(@Valid @RequestBody TestForm testForm){
        return R.ok().put("message","hello,"+testForm.getName());
    }

}

抵御跨站脚本(XSS)攻击[亲测有效]

今天就学习到这里,这几天正在看Rabbitmq的相关知识,后面整理给大家,晚安😴

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/13668.html

(0)

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注