SpringBoot去除引數前後空格和XSS過濾
阿新 • • 發佈:2021-09-03
去除XSS字串需要藉助工具類 oup ,這裡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.工具類:
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 { @Override public void doFilter(ServletRequest arg0,ServletResponse arg1,FilterChain arg2) throws IOException,ServletException { ParameterRequestWrapper parmsRequest = new ParameterRequestWrapper( (HttpServletRequest) arg0); arg2.doFilter(parmsRequest,arg1); } @Override public void init(FilterConfig arg0) throws ServletException { } @Override public 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; @Configuration public class ParameterTrimConfig { /** * 去除引數頭尾空格過濾器 * @return */ @Bean public 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 : 請求引數的處理, * 1. 去除引數前後空格 * 2. 過濾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型別的請求引數必須通過流才能獲取到值 */ @Override public 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(),Stwww.cppcns.comandardCharsets.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獲取 */ @Override public String getParameter(String name) { String[]values = params.get(name); if(values == null || values.length == 0) { return null; } return values[0]; } /** * 重寫getParameterValues */ @Override public 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 * 1. 去除Json字串中的屬性值前後空格的工具類 * 2. 去除 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 jsonOQYAdAcFRLLbject){ // 取出 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 的jsonObjwww.cppcns.comect 中的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 jsoQYAdAcFRLLnObject = (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 { private static final long serialVersionUID = 1L; @NotBlank(message = "testStr不能為空") @ApiModelProperty("testStr") private String testStr; } }
上面接口裡面的ApiResult為自定義封裝的返回資料格式,可以直接修改介面返回型別改為String,就是為了看下後臺處理後,返回前臺是什麼樣的。
form傳參,過濾成功。
JSON傳參,也沒毛病。
到此這篇關於SpringBoot去除引數前後空格和XSS過濾的文章就介紹到這了,更多相關SpringBoot去除引數前後空格和XSS過濾內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!