抵御即跨站脚本(XSS)攻击

一、XSS攻击的危害

XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java、VBScript、ActiveX、Flash或者甚至是普通的HTML。攻击成功后,
攻击者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。
例如用户在发帖或者注册的时候,在文本框中输入,这段代码如果不经过转义处理,而直接保存到数据库。将来视图层渲染HTML的时候,把这段代码输出到页面上,那么

二、导入依赖库

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.4.0</version>
</dependency>

三、定义请求包装类

package com.example.emos.wx.config.xss;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HtmlUtil;
import cn.hutool.json.JSONUtil;

import javax.print.DocFlavor;
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.LinkedHashMap;
import java.util.Map;

public class XssHttpServeletRequestWrapper extends HttpServletRequestWrapper {
    public XssHttpServeletRequestWrapper(HttpServletRequest request){
        super(request);
    }

    @Override
    public String getParameter(String name){
        String value = super.getParameter(name); //获取请求中的原始数据
        if(!StrUtil.hasEmpty(value)){  //数据不为空
            value = HtmlUtil.filter(value);  //把数据做转义,把html、script标签去掉,变成普通的文本
        }
        return value;
    }

    /**转义数组中的数据*/
    @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.hasEmpty(value)){
                    value = HtmlUtil.filter(value);
                }
                values[i] = value;
            }
        }
        return values;
    }

    @Override
    public Map<String, String[]> getParameterMap(){
        Map<String,String[]> parameters = super.getParameterMap();
        LinkedHashMap<String,String[]> map = new LinkedHashMap();
        if(parameters != null){
            for(String key:parameters.keySet()){
                String[] values = parameters.get(key);
                for(int i=0;i<values.length;i++){
                    String value = values[i];
                    if(!StrUtil.hasEmpty(value)){
                        value = HtmlUtil.filter(value);
                    }
                    values[i] = value;
                }
                map.put(key,values);
            }
        }
        return map;
    }

    @Override
    public String getHeader(String name) {
        String value = super.getHeader(name);
        if(!StrUtil.hasEmpty(value)){
            value = HtmlUtil.filter(value);
        }
        return value;
    }

    @Override
    public ServletInputStream getInputStream() throws IOException{
        InputStream in = super.getInputStream();
        InputStreamReader reader = new InputStreamReader(in, Charset.forName("UTF-8"));
        BufferedReader buffer = new BufferedReader(reader);
        StringBuffer body = new StringBuffer();
        String line = buffer.readLine();
        while (line != null){
            body.append(line);
            line = buffer.readLine();
        }
        buffer.close();
        reader.close();
        in.close();
        Map<String,Object> map = JSONUtil.parseObj(body.toString());
        Map<String,Object> resultMap = new LinkedHashMap<>();
        for(String key : map.keySet()){
            Object val = map.get(key);
            if(map.get(key) instanceof String){
                if(!StrUtil.hasEmpty(val.toString())){
                    resultMap.put(key, HtmlUtil.filter(val.toString()));
                }
            }else{
                resultMap.put(key, val);
            }
        }
        String str = JSONUtil.toJsonStr(resultMap);
        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 readListener) {

            }
        };


    }
}

四、创建过滤器,把所有请求对象传入包装类

package com.example.emos.wx.config.xss;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@WebFilter(urlPatterns = "/*")   //拦截请求的路径
public class XssFilter implements Filter {
    public void init(FilterConfig config) throws ServletException{

    }

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

    @Override
    public void destroy() {

    }


}

五、给主类添加注解

给SpringBoot主类添加@ServletComponentScan注解

六、测试拦截XSS脚本

测试controller

@RestController
@RequestMapping("/test")
@Api("测试Web接口")
public class TestController {

    @PostMapping("/sayHello")
    @ApiOperation("最简单的测试方法")
    public R sayHello(@Valid @RequestBody TestSayHelloForm form){
        return R.ok().put("message","Hello,"+form.getName());
    }
}

@ApiModel
@Data
public class TestSayHelloForm {

    @NotBlank
    private String name;
}

在Swagger中执行sayHello()方法,向name属性传入<script>alert(123456)</script>观察返回结果
抵御即跨站脚本(XSS)攻击
抵御即跨站脚本(XSS)攻击

上一篇:XSS平台搭建及利用


下一篇:Ionic入门四:卡片