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

3 篇文章 0 订阅
2 篇文章 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
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
Spring Cloud Gateway可以通过编写自定义的过滤器实现XSS过滤。 首先,我们需要创建一个XSS过滤器类,实现`GlobalFilter`和`Ordered`接口: ```java @Component public class XssGlobalFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); HttpHeaders headers = request.getHeaders(); MediaType contentType = headers.getContentType(); HttpMethod method = request.getMethod(); if (contentType != null && contentType.isCompatibleWith(MediaType.APPLICATION_JSON) && HttpMethod.POST.equals(method)) { return chain.filter(exchange.mutate().request(new XssServerHttpRequest(request)).build()); } return chain.filter(exchange); } @Override public int getOrder() { return -1; } } ``` 这里,我们首先判断请求的Content-Type是否为`application/json`,并且请求方法是否为POST,如果是,则将请求的`ServerHttpRequest`替换为我们自定义的`XssServerHttpRequest`,该类继承自`ServerHttpRequestDecorator`,在该类中对请求进行XSS过滤,代码如下: ```java public class XssServerHttpRequest extends ServerHttpRequestDecorator { public XssServerHttpRequest(ServerHttpRequest delegate) { super(delegate); } @Override public Flux<DataBuffer> getBody() { Flux<DataBuffer> body = super.getBody(); return body.map(dataBuffer -> { CharBuffer charBuffer = StandardCharsets.UTF_8.decode(dataBuffer.asByteBuffer()); String bodyContent = charBuffer.toString(); // 进行XSS过滤 String filteredBodyContent = Jsoup.clean(bodyContent, Whitelist.none()); byte[] bytes = filteredBodyContent.getBytes(StandardCharsets.UTF_8); DataBuffer buffer = new DefaultDataBufferFactory().wrap(bytes); DataBufferUtils.release(dataBuffer); return buffer; }); } } ``` 在该类中,我们首先将`DataBuffer`转换成`CharBuffer`,再将其转换成字符串,然后使用Jsoup对字符串进行XSS过滤,最后再将过滤后的字符串转换成`DataBuffer`返回。 最后,我们需要将这个过滤器添加到Spring Cloud Gateway的过滤器链中,在配置类中添加: ```java @Configuration public class GatewayConfig { @Bean public XssGlobalFilter xssGlobalFilter() { return new XssGlobalFilter(); } @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() // 添加自定义路由 .route(r -> r.path("/api/**").uri("lb://service-provider")) .build(); } } ``` 这样,当请求Content-Type为`application/json`,并且请求方法为POST时,请求体中的HTML标签就会被过滤掉,从而实现XSS过滤

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值