spring利用filter进行xss过滤(包含post请求)以及请求入参日志输出

spring 同时被 3 个专栏收录
3 篇文章 0 订阅
2 篇文章 0 订阅
10 篇文章 0 订阅

背景:过滤xss攻击,同时将过滤后的日志输出到指定文件。(指定文件输出请看上篇博文)

前景:利用filter进行xss攻击过滤,需要应对不同请求做不同的过滤处理,若是post请求的json格式数据,需要重写getinputstream方法(因为流读取一次后,下层controller无法再次进行读取。原理可自行百度)

因此需要重写两个wrapper(继承HttpServletRequestWrapper)(针对post的json请求以及其他格式的请求)

在filter中以contentType做类型的区分

未做业务耦合的区分(日志输出与xss过滤耦合到一起),若要区分开功能,单独书写即可

一:重写wrapper

1:contentType为multipart/form-data或application/x-www-form-urlencoded

public class XSSNormalRequestWrapper extends HttpServletRequestWrapper {


	public XSSNormalRequestWrapper(HttpServletRequest request) {
		super(request);
	}

	@Override
	public String[] getParameterValues(String parameter) {
		String[] values = super.getParameterValues(parameter);
		if (values == null) {
			return null;
		}
		int count = values.length;
		String[] encodedValues = new String[count];
		for (int i = 0; i < count; i++) {
			encodedValues[i] = stripXSS(values[i]);
		}
		return encodedValues;
	}

	@Override
	public String getParameter(String parameter) {
		String value = super.getParameter(parameter);
		return stripXSS(value);
	}

	@Override
	public String getHeader(String name) {
		String value = super.getHeader(name);
		return stripXSS(value);
	}

}

2:contentType为application/json

public class XSSBodyRequestWrapper extends HttpServletRequestWrapper {

    private String body;

    public XSSBodyRequestWrapper(HttpServletRequest request) {
        super(request);
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;
        InputStream inputStream = null;
        try {
            inputStream = request.getInputStream();
            if (inputStream != null) {
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                char[] charBuffer = new char[128];
                int bytesRead = -1;
                while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                    stringBuilder.append(charBuffer, 0, bytesRead);
                }
            } else {
                stringBuilder.append("");
            }
        } catch (IOException ex) {

        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        body = XSSStripUtils.stripXSS(stringBuilder.toString());
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
        ServletInputStream servletInputStream = new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

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

            @Override
            public void setReadListener(ReadListener readListener) {

            }

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

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }

    public String getBody() {
        return body;
    }


}

二:xss处理工具方法:

public class XSSStripUtils {

    public static String stripXSS(String value) {
        if (value != null) {
            Pattern scriptPattern = Pattern.compile("<script>(\\s*.*?)</script>",
                    Pattern.CASE_INSENSITIVE);
            value = scriptPattern.matcher(value).replaceAll("-");
            scriptPattern = Pattern.compile("</script(\\s*.*?)>",
                    Pattern.CASE_INSENSITIVE);
            value = scriptPattern.matcher(value).replaceAll("-");
            scriptPattern = Pattern.compile("<script(\\s*.*?)>",
                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
                            | Pattern.DOTALL);
            value = scriptPattern.matcher(value).replaceAll("-");
            scriptPattern = Pattern.compile("eval\\((.*?)\\)",
                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
                            | Pattern.DOTALL);
            value = scriptPattern.matcher(value).replaceAll("-");
            scriptPattern = Pattern.compile("e­xpression\\((.*?)\\)",
                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
                            | Pattern.DOTALL);
            value = scriptPattern.matcher(value).replaceAll("-");
            scriptPattern = Pattern.compile("javascript:",
                    Pattern.CASE_INSENSITIVE);
            value = scriptPattern.matcher(value).replaceAll("-");
            scriptPattern = Pattern.compile("vbscript:",
                    Pattern.CASE_INSENSITIVE);
            value = scriptPattern.matcher(value).replaceAll("-");
            scriptPattern = Pattern.compile("onload(.*?)=",
                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
                            | Pattern.DOTALL);
            value = scriptPattern.matcher(value).replaceAll("-");


            scriptPattern = Pattern.compile("<+.*(oncontrolselect|oncopy|oncut|ondataavailable|ondatasetchanged|ondatasetcomplete|ondblclick|ondeactivate|ondrag|ondragend|ondragenter|ondragleave|ondragover|ondragstart|ondrop|onerror|onerroupdate|onfilterchange|onfinish|onfocus|onfocusin|onfocusout|onhelp|onkeydown|onkeypress|onkeyup|onlayoutcomplete|onload|onlosecapture|onmousedown|onmouseenter|onmouseleave|onmousemove|onmousout|onmouseover|onmouseup|onmousewheel|onmove|onmoveend|onmovestart|onabort|onactivate|onafterprint|onafterupdate|onbefore|onbeforeactivate|onbeforecopy|onbeforecut|onbeforedeactivate|onbeforeeditocus|onbeforepaste|onbeforeprint|onbeforeunload|onbeforeupdate|onblur|onbounce|oncellchange|onchange|onclick|oncontextmenu|onpaste|onpropertychange|onreadystatechange|onreset|onresize|onresizend|onresizestart|onrowenter|onrowexit|onrowsdelete|onrowsinserted|onscroll|onselect|onselectionchange|onselectstart|onstart|onstop|onsubmit|onunload)+.*=+",
                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
                            | Pattern.DOTALL);
            value = scriptPattern.matcher(value).replaceAll("-");



            // 过滤emoji表情
            scriptPattern = Pattern
                    .compile(
                            "[\ud83c\udc00-\ud83c\udfff]|[\ud83d\udc00-\ud83d\udfff]|[\u2600-\u27ff]",
                            Pattern.UNICODE_CASE | Pattern.CASE_INSENSITIVE);
            value = scriptPattern.matcher(value).replaceAll("-");
        }
        return value;
    }

}

三:filter过滤器

public class XSSFilter implements Filter{

	private static final Logger logger = LoggerFactory.getLogger("external_request");

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

	}


	@Override
	public void doFilter(ServletRequest servletRequest, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) servletRequest;
		resetCookies(request,(HttpServletResponse)response);
		String contentType = request.getContentType();
		if (StringUtils.isNotBlank(contentType) && contentType.contains("multipart/form-data")) {
			MultipartHttpServletRequest multipartHttpServletRequest =
					new CommonsMultipartResolver().resolveMultipart(request);
			XSSNormalRequestWrapper xssNormalRequestWrapper = new XSSNormalRequestWrapper(multipartHttpServletRequest);
			logUrlAndParam(request,xssNormalRequestWrapper,null);
			chain.doFilter(xssNormalRequestWrapper, response);
		} else if (StringUtils.isNotBlank(contentType) && contentType.contains("application/x-www-form-urlencoded")) {
			XSSNormalRequestWrapper xssNormalRequestWrapper = new XSSNormalRequestWrapper(request);
			logUrlAndParam(request,xssNormalRequestWrapper,null);
			chain.doFilter(xssNormalRequestWrapper, response);
		} else if (StringUtils.isNotBlank(contentType) && contentType.contains("application/json")) {
			XSSBodyRequestWrapper xssBodyRequestWrapper = new XSSBodyRequestWrapper(request);
			logUrlAndParam(request,null,xssBodyRequestWrapper);
			chain.doFilter(xssBodyRequestWrapper, response);
		} else {
			logUrlAndParam(request,null,null);
			chain.doFilter(request, response);
		}

	}

	public Cookie[] resetCookies(HttpServletRequest request,
								 HttpServletResponse response) {
		Cookie[] cookies = request.getCookies();
		Map<String, String> map = new HashMap<>();
		if (cookies != null && cookies.length != 0) {
			for (Cookie cookie : cookies) {
				String value = cookie.getValue();
				if (value != null) {
					String deleteValue = stripXSS(value);
					if (!value.equals(deleteValue)) {
						map.put(cookie.getName(), cookie.getDomain());
					}
				}
			}

			if (CollectionUtils.isNotEmpty(map)) {
				for (Map.Entry<String, String> cookie : map.entrySet()) {
					CookieUtil.removeCookie(request, response, cookie
							.getValue(), cookie.getKey());
				}
			}
		}
		return cookies;
	}

	@Override
	public void destroy() {
		
	}

	private void logUrlAndParam(HttpServletRequest request, XSSNormalRequestWrapper xssNormalRequestWrapper,XSSBodyRequestWrapper xssBodyRequestWrapper) {
		// 获取请求URL
		String url = request.getRequestURI();
		// 获取请求方法
		String method = request.getMethod();
		// 获取path
		String path = request.getQueryString();
		// 获取header
		Map<String, String> map = new HashMap<String, String>();
		Enumeration headerNames = request.getHeaderNames();
		while (headerNames.hasMoreElements()) {//循环遍历Header中的参数,把遍历出来的参数放入Map中
			String key = (String) headerNames.nextElement();
			String value =  request.getHeader(key);
			map.put(key, value);
		}
		String header = JSON.toJSONString(map);
		// 获取参数
		String params = "";
		if(xssNormalRequestWrapper != null) {
			params = JSON.toJSONString(xssNormalRequestWrapper.getParameterMap());
		}
		if(xssBodyRequestWrapper != null) {
			params = xssBodyRequestWrapper.getBody();
		}
		if(xssNormalRequestWrapper == null && xssBodyRequestWrapper == null) {
			params = JSON.toJSONString(request.getParameterMap());
		}
		logger.info("请求url:{},方法:{},path:{},header:{},param:{}",url,method,path,header,params);
	}


}

四:在web.xml配置过滤器

	<filter>
		<filter-name>XSSFilter</filter-name>
		<filter-class>xxxx.filter.XSSFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>XSSFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

 

  • 1
    点赞
  • 4
    评论
  • 10
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值