background

  • Recently, when the information security department of the company upgraded the network security of the company’s project, it found that the XSS script attack vulnerability might appear in the project, so it needed to filter and intercept its parameters.

XSS

  • XSS attack Full name: Cross Site Scripting (XSS), cross Site scripting (XSS), is the most common Web application security vulnerability. This vulnerability enables attackers to embed malicious script codes into pages that normal users will access. When normal users access this page, the embedded malicious script codes will be executed, so as to achieve the purpose of malicious attacks on users. An attacker can execute predefined malicious scripts in the browser, which can cause predictable harm, such as hijacking user sessions, inserting malicious content, redirecting users, hijacking users’ browsers with malicious software, propagating XSS worms, and even damaging websites and modifying router configuration information.
  • XSS vulnerability attacks fall into three categories:
    • Reflective XSS attack: When the front end sends a request, it carries some script commands in THE URL parameters, and then waits for the server to reflect the script to the browser to execute the script code, so as to carry out XSS vulnerability attack
    • Storage XSS attack: Similar to the reflexive XSS vulnerability attack, but the server will persist the script command, and the subsequent user access to the data will be persistent XSS attack
    • DOS XSS attack: XSS attack is carried out by DOM parsing of the browser without interaction with the server

Java filter

  • In addition to parsing and filtering on the Web, verification and filtering on the server are also required to prevent XSS vulnerability attacks
  • This time, the filter in the Springboot project is used to filter and intercept the parameters from the front end
/** * springBoot register filter ** @author: ZRH * @date: 2021-11-17 */ @Configuration public class XssFilterConfig { @Bean public FilterRegistrationBean xssFilterRegistrationBean () { FilterRegistrationBean initXssFilterBean = new FilterRegistrationBean(); / / set custom filters initXssFilterBean. SetFilter (new XssFilter ()); / / set the priority value is lower, the higher the priority) initXssFilterBean. SetOrder (1); / / set filter path initXssFilterBean addUrlPatterns (" / * "); / / set the filter name initXssFilterBean. Elegantly-named setName (" XSS_filter "); / filter/set scope (can be furnished with a variety of, here specify filter REQUEST resources) initXssFilterBean. SetDispatcherTypes (DispatcherType. REQUEST); return initXssFilterBean; }}Copy the code
  • Spring projects can define filters using Web. XML, and springBoot does not have a web. XML configuration file, so you can use the FilterRegistrationBean class to register filter instances with the container
/** * custom filter ** @author: ZRH * @date: 2021-11-17 */ @Slf4j public class XssFilter implements Filter { @Override public void init (FilterConfig filterConfig) { } @override public void doFilter (ServletRequest ServletRequest, ServletResponse ServletResponse, FilterChain filterChain) throws IOException, ServletException { final XssHttpServletRequestWrapper requestWrapper = new XssHttpServletRequestWrapper((HttpServletRequest) servletRequest); filterChain.doFilter(requestWrapper, servletResponse); } @override public void destroy () {// destroy call}}Copy the code
** @author: ZRH * @date: 2021-11-17 */ @Slf4j public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { public XssHttpServletRequestWrapper (HttpServletRequest request) { super(request); } @param name * @return */ @override public String[] getParameterValues (String name) { String[] values = super.getParameterValues(name); if (values == null) { return null; } int count = values.length; String[] cleanParams = new String[count]; for (int i = 0; i < count; i++) { cleanParams[i] = String.valueOf(XssUtil.filterParam(values[i])); Log.info ("getParameterValues -> name: {}, name, values[I], cleanParams[I], cleanParams[I]); } return cleanParams; } @param header @return */ @Override public Enumeration getHeaders (String header) {final String value = super.getHeader(header); final LinkedList list = new LinkedList(); if (value ! = null) { final Object param = XssUtil.filterParam(value); list.addFirst(param); Log.info ("getHeaders -> header: {}", header, value, param); } return Collections.enumeration(list); @override public ServletInputStream getInputStream (); throws IOException { final ByteArrayInputStream stream = new ByteArrayInputStream(inputHandlers(super.getInputStream()).getBytes()); return new ServletInputStream() { @Override public int read () { return stream.read(); } @Override public boolean isFinished () { return false; } @Override public boolean isReady () { return false; } @Override public void setReadListener (ReadListener readListener) { } }; } @param servletInputStream @return */ public String inputHandlers (servletInputStream servletInputStream) { StringBuilder sb = new StringBuilder(); BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader(servletInputStream, Charset.forName("UTF-8"))); String line; while ((line = reader.readLine()) ! = null) { sb.append(line); }} catch (IOException e) {log.error(" e: ", e); } finally { if (servletInputStream ! = null) { try { servletInputStream.close(); } catch (IOException e) {log.error("servletInputStream closed exception e: ", e); } } if (reader ! = null) { try { reader.close(); } catch (IOException e) {log.error("reader closed error e: ", e); } } } final String param = XssUtil.filterBody(sb.toString()); Log.info ("getInputStream -> before: {}, after: {}", sb, param); return param; }}Copy the code
  • Rewrite HttpServletRequestWrapper classes are used to filter change request parameter values
** @author: ZRH * @date: 2021-11-17 */ @slf4j public Final class XssUtil {private XssUtil () {} /** * private final static Pattern[] PATTERNS = new Pattern[]{ // Script fragments Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE), // src='... ' Pattern.compile("src[\r\n]*=[\r\n]*\'(.*?)\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), Pattern.compile("src[\r\n]*=[\r\n]*\"(.*?)\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), // lonely script tags Pattern.compile("</script>", Pattern.CASE_INSENSITIVE), Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), // eval(...) Pattern.compile("eval\((.*?)\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), // expression(...) Pattern.compile("expression\((.*?)\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), // javascript:... Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE), // vbscript:... Pattern.pile (" vbScript :", pattern.case_insensitive), // Blank English single double quotes pattern.pile ("[\s'"]+", Pattern.CASE_INSENSITIVE), // onload(...) =... Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), // alert Pattern.compile("alert(.*?)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), Pattern.compile("<", Pattern.MULTILINE | Pattern.DOTALL), Pattern.compile(">", Pattern.MULTILINE | Pattern.DOTALL), //Checks any html tags i.e. <script, <embed, <object etc. Pattern.compile("(<(script|iframe|embed|frame|frameset|object|img|applet|body|html|style|layer|link|ilayer|meta|bgsound) ) ")}; ** @param params * @return */ public static String filterBody (String params) {try {if (StringUtils.isBlank(params)) { return params; } final Map<String, Object> map = JSONObject.parseObject(params, Map.class); if (map.isEmpty()) { return params; } // Final Iterator< map.entry <String, Object>> Iterator = map.entryset ().stream().iterator(); while (iterator.hasNext()) { final Map.Entry<String, Object> next = iterator.next(); next.setValue(filterParam(next.getValue())); } return JSON.toJSONString(map); } catch (Exception e) {log.error("XSS: ", e); } return params; } @param param @param <T> * @return */ public static <T> Object filterParam (T param) {if (param instanceof String) { try { String value = String.valueOf(param); for (Pattern pattern : PATTERNS) { value = pattern.matcher(value).replaceAll(""); } return value; } catch (Exception e) {log.error("XSS: ", e); } } return param; }}Copy the code
  • XSS regular utility class for filtering parameters
/** * * @Author: ZRH * @Date: 2021/11/17 */ @RestController public class XssTest { @PostMapping("/xss/test") public String test (@RequestBody JSONObject jsonObject) { System.out.println(jsonObject.toJSONString()); return "OK"; } @GetMapping("/xss/test") public String test (@RequestParam Integer data, @RequestParam String result) { System.out.println(data); return "OK"; }} 17:07:06.597 - [http-nio-8888-exec-1] -getheaders -> header: content-type Application /json 17:07:06.598 - [http-nio-8888-exec-1] -getheaders -> header: token 123 17:07:06.598 - [http-nio-8888-exec-1] -getheaders -> update -> update -> update -> update -> update 123 17:07:06.598 - [http-nio-8888-exec-1] -getheaders -> header: b <script>alert("XSS"); </script>, 17:07:06.599 - [http-nio-8888-exec-1] -getheaders -> header: content-length 67 17:07:06.599 - [http-nio-8888-exec-1] -getheaders -> header: host Localhost :8888 17:07:06.599 - [http-nio-8888-exec-1] -getheaders -> header: connection Keep-alive 17:07:06.599 - [http-nio-88888-exec-1] -getheaders -> header: user-agent Apache-httpclient /4.5.12 (Java/11.0.8) Apache-httpclient /4.5.12(Java/11.0.8) 17:07:06.599 - [http-nio-88888-exec-1] - getHeaders -> header: [http-niO-88888-exec-1] -getinputStream -> Gzip,deflate 17:07:06.648 - [http-nio-88888-exec-1] -getinputStream -> Gzip,deflate 17:07:06.648 - [http-nio-88888-exec-1] -getinputStream -> Gzip,deflate 17:07:06.648 - [http-nio-88888-exec-1] -getinputStream -> { "a": "1", "b": 2, "c": "<script>alert(\"XSS\"); < / script > "}, filtered parameters: {" a ":" 1 ", "b" : 2, "c" : ""} {" a" : "1", "b" : 2, "c" : ""}Copy the code

The last

  • In addition to being able to use filters, you can also use interceptors for interception verification. The process is similar: parameters are fetched, then validated, and finally reassigned.
  • The above code example is only a simple rough version, which can be used online only after code adjustment and performance optimization according to requirements in actual projects.
  • Learn with an open mind and make progress together