写点什么

So eazy!SpringBoot 一键去除参数前后空格和 XSS 过滤实战解析

  • 2021 年 11 月 19 日
  • 本文字数:5550 字

    阅读完需:约 18 分钟

去除 XSS 字符串需要借助工具类 jsoup ,这里 jsoup 有一点需要注意的是,jsoup 的功能可能有点太强大了,能把 xss 攻击的内容直接过滤掉了不说,也会对英文尖括号 <> 转义,到接口里面拿到的参数就变成了 <> ,存库里面的就是转义后的字符串了。取出来的时候需要转一下。


比如前台传的参数传的是: 12<>3<script>alter('11111111')</script>455


过滤处理了后,到后台接口里面就成了: [12<>3455]


如果上面的结果能接受,那么这个工具类就可以用。


引入依赖 jsoup


<dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.12.1</version></dependency>复制代码 JsoupUtil.java 工具类:


import org.jsoup.Jsoup;import org.jsoup.nodes.Document;import org.jsoup.safety.Whitelist;


/***


  • @Auther linmengmeng

  • @Date 2021-08-19 15:47

  • 描述: 过滤 HTML 标签中 XSS 代码/public class JsoupUtil {/*

  • 使用自带的 basicWithImages 白名单

  • 允许的便签有 a,b,blockquote,br,cite,code,dd,dl,dt,em,i,li,ol,p,pre,q,small,span,strike,strong,sub,sup,u,ul,img

  • 以及 a 标签的 href,img 标签的 src,align,alt,height,width,title 属性/private static final Whitelist whitelist = Whitelist.basicWithImages();/* 配置过滤化参数, 不对代码进行格式化 */private static final Document.OutputSettings outputSettings = new Document.OutputSettings().prettyPrint(false);static {// 富文本编辑时一些样式是使用 style 来进行实现的// 比如红色字体 style="color:red;"// 所以需要给所有标签添加 style 属性 whitelist.addAttributes(":all", "style");}public static String clean(String content) {return Jsoup.clean(content, "", whitelist, outputSettings);}}复制代码首先是定义参数过滤器: ParamsFilter 实现 Filter 类


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


/**


  • Description : 参数过滤器


*/public class ParamsFilter implements Filter {


@Overridepublic void doFilter(ServletRequest arg0, ServletResponse arg1,                     FilterChain arg2) throws IOException, ServletException {    ParameterRequestWrapper parmsRequest = new ParameterRequestWrapper(            (HttpServletRequest) arg0);    arg2.doFilter(parmsRequest, arg1);}
@Overridepublic void init(FilterConfig arg0) throws ServletException {
}
@Overridepublic void destroy() {
}
复制代码


}复制代码添加参数过滤配置文件:


import gc.cnnvd.framework.core.filter.ParamsFilter;import org.springframework.boot.web.servlet.FilterRegistrationBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;


import javax.servlet.DispatcherType;


@Configurationpublic class ParameterTrimConfig {


/** * 去除参数头尾空格过滤器 * @return */@Beanpublic FilterRegistrationBean parmsFilterRegistration() {    FilterRegistrationBean registration = new FilterRegistrationBean();    registration.setDispatcherTypes(DispatcherType.REQUEST);    registration.setFilter(new ParamsFilter());    registration.addUrlPatterns("/*");    registration.setName("paramsFilter");    registration.setOrder(Integer.MAX_VALUE-1);    return registration;}
复制代码


}复制代码处理都交给了这货:


import com.alibaba.fastjson.JSON;import gc.cnnvd.framework.config.converter.JsonValueTrimUtil;import gc.cnnvd.framework.util.JsoupUtil;import org.apache.commons.io.IOUtils;import org.apache.commons.lang3.StringUtils;import org.springframework.http.HttpHeaders;import org.springframework.http.MediaType;


import javax.servlet.ReadListener;import javax.servlet.ServletInputStream;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletRequestWrapper;import java.io.ByteArrayInputStream;import java.io.IOException;import java.nio.charset.StandardCharsets;import java.util.HashMap;import java.util.Iterator;import java.util.Map;import java.util.Set;


/***


  • @Auther linmengmeng

  • @Date 2021-03-25 15:47

  • Description : 请求参数的处理,

  • 去除参数前后空格

  • 过滤 XSS 非法字符


*/public class ParameterRequestWrapper extends HttpServletRequestWrapper {


private Map<String , String[]> params = new HashMap<>();

public ParameterRequestWrapper(HttpServletRequest request) { // 将request交给父类,以便于调用对应方法的时候,将其输出,其实父亲类的实现方式和第一种new的方式类似 super(request); //将参数表,赋予给当前的Map以便于持有request中的参数 Map<String, String[]> requestMap=request.getParameterMap();
this.params.putAll(requestMap); this.modifyParameterValues();
}/** * 重写getInputStream方法 post类型的请求参数必须通过流才能获取到值 */@Overridepublic ServletInputStream getInputStream() throws IOException { //非json类型,直接返回 if (!MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(super.getHeader(HttpHeaders.CONTENT_TYPE))){ return super.getInputStream(); } //为空,直接返回 String json = IOUtils.toString(super.getInputStream(), StandardCharsets.UTF_8); if (StringUtils.isEmpty(json)) { return super.getInputStream(); } Object resultObject = JsonValueTrimUtil.jsonStrTrim(json);//这里处理的是json传参的参数 ByteArrayInputStream bis = new ByteArrayInputStream(JSON.toJSONString(resultObject).getBytes(StandardCharsets.UTF_8)); return new CustomServletInputStream(bis);
}/** * 将parameter的值去除空格后重写回去 */public void modifyParameterValues(){ Set<String> set = params.keySet(); Iterator<String> it = set.iterator(); while(it.hasNext()){ String key = it.next(); String[] values = params.get(key); values[0] = values[0].trim(); values[0] = JsoupUtil.clean(values[0]);//这里处理的是form传参的参数 params.put(key, values); }}/** * 重写getParameter 参数从当前类中的map获取 */@Overridepublic String getParameter(String name) { String[]values = params.get(name); if(values == null || values.length == 0) { return null; } return values[0];}/** * 重写getParameterValues */@Overridepublic String[] getParameterValues(String name) {//同上 return params.get(name);}
class CustomServletInputStream extends ServletInputStream{ private ByteArrayInputStream bis; public CustomServletInputStream(ByteArrayInputStream bis){ this.bis=bis; } @Override public boolean isFinished() { return true; }
@Override public boolean isReady() { return true; }
@Override public void setReadListener(ReadListener listener) {
} @Override public int read() throws IOException { return bis.read(); }}
复制代码


}复制代码上面 form 传的参数直接处理了,那么要是 JSON 传的参数,就要借助下面的工具类了:


import cn.hutool.json.JSONTokener;import com.alibaba.fastjson.JSONArray;import com.alibaba.fastjson.JSONObject;import gc.cnnvd.framework.util.JsoupUtil;import org.apache.commons.lang3.StringUtils;


import java.util.Map;import java.util.Set;


/**


  • @Auther linmengmeng

  • @Date 2021-03-25 15:47

  • 去除 Json 字符串中的属性值前后空格的工具类

  • 去除 XSS 攻击字符*/


public class JsonValueTrimUtil {


public static Object jsonStrTrim(String jsonStr){    if (StringUtils.isBlank(jsonStr)){        return "";    }    Object typeObject = new JSONTokener(jsonStr).nextValue();    if (typeObject instanceof cn.hutool.json.JSONObject){        return jsonObjectTrim(JSONObject.parseObject(jsonStr));    }    if (typeObject instanceof cn.hutool.json.JSONArray){        return jsonArrayTrim(JSONArray.parseArray(jsonStr));    }    jsonStr = JsoupUtil.clean(jsonStr);    return jsonStr.trim();}
/** * @Description: 传入jsonObject 去除当中的空格 * @param jsonObject * @return */public static JSONObject jsonObjectTrim(JSONObject jsonObject){ // 取出 jsonObject 中的字段的值的空格 Set<Map.Entry<String, Object>> entrySets = jsonObject.entrySet(); entrySets.forEach(entry -> { Object value = entry.getValue(); if (value == null){ return; } if (value instanceof String) { String resultValue = (String) value; if (StringUtils.isNotBlank(resultValue)){ resultValue = resultValue.trim(); resultValue = JsoupUtil.clean(resultValue); jsonObject.put(entry.getKey(), resultValue); } return; } if (value instanceof JSONObject){ jsonObject.put(entry.getKey(), jsonObjectTrim((JSONObject) value)); return; } if (value instanceof JSONArray){ jsonObject.put(entry.getKey(), jsonArrayTrim((JSONArray) value)); return; } }); return jsonObject;}
/** * @Description: 将 jsonarry 的jsonObject 中的value值去处前后空格 * @param arr * @return */public static JSONArray jsonArrayTrim(JSONArray arr){ if( arr != null && arr.size() > 0){ Object tempObject = null; for (int i = 0; i < arr.size(); i++) { tempObject = arr.get(i); if (tempObject instanceof String){ arr.set(i, tempObject ); continue; } JSONObject jsonObject = (JSONObject) arr.get(i); // 取出 jsonObject 中的字段的值的空格 jsonObject = jsonObjectTrim(jsonObject); arr.set(i, jsonObject ); } } return arr;}
复制代码


}复制代码测试一下:


import gc.cnnvd.framework.common.api.ApiResult;import gc.cnnvd.framework.log.annotation.OperationLogIgnore;import io.swagger.annotations.Api;import io.swagger.annotations.ApiModel;import io.swagger.annotations.ApiModelProperty;import io.swagger.annotations.ApiOperation;import lombok.Data;import lombok.experimental.Accessors;import lombok.extern.slf4j.Slf4j;import org.springframework.validation.annotation.Validated;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;


import javax.validation.constraints.NotBlank;import java.io.Serializable;


/**


  • @Auther linmengmeng

  • @Date 2021-03-30 16:24*/@Slf4j@RestController@RequestMapping("/tourist")@Api(value = "测试游客页面 xss", tags = {"游客页面 xss"})public class TouristTestXssController {

  • @PostMapping("/testXssForm")@OperationLogIgnore@ApiOperation(value = "测试放开游客页面接口 xssForm", notes = "测试放开游客页面接口 xssForm", response = String.class)public ApiResult<String> testXss(TestXssFormParam testXssFormParam) {log.info("form param testStr:[{}]", testXssFormParam.getTestStr());// from 不好使 return ApiResult.ok(testXssFormParam.getTestStr());}

  • @PostMapping("/testXssJson")@OperationLogIgnore@ApiOperation(value = "测试放开游客页面接口 xssJson", notes = "测试放开游客页面接口 xssJson", response = String.class)public ApiResult<String> testXssJson(@Validated @RequestBody TestXssFormParam testXssFormParam) {log.info("json param testStr:[{}]", testXssFormParam.getTestStr());return ApiResult.ok(testXssFormParam.getTestStr());}

  • @Data@Accessors(chain = true)@ApiModel(value = "testXss 参数")public static class TestXssFormParam implements Serializable {

  • }


}复制代码上面接口里面的 ApiResult 为自定义封装的返回数据格式,可以直接修改接口返回类型改为 String,就是为了看下后台处理后,返回前台是什么样的。


form 传参,过滤成功。


So eazy!SpringBoot 一键去除参数前后空格和 XSS 过滤实战解析 JSON 传参,也没毛病。


So eazy!SpringBoot 一键去除参数前后空格和 XSS 过滤实战解析

用户头像

还未添加个人签名 2021.10.14 加入

还未添加个人简介

评论

发布
暂无评论
So eazy!SpringBoot一键去除参数前后空格和XSS过滤实战解析