注意:
這個xss過濾器有一些問題,比如某些時候,使用jquery ajax post的時候,如果是傳的默認的 "application/x-www-form-urlencoded",會出現Controller中的bean接收的值為null的情況,
當我調整ajax設置 contentType:"application/json",dataType:"json" 發送一個帶post json數據時,xss攔截器報錯,於是刪除整個xss包,不再使用這四個類,jquery ajax post一切正常,可以以默認方式提交。(之前只能用axios去拼接參數到url里來實現post(將拼接的參數存入一個變量))
總共4個類,放在config.safe.xss包下了:
package org.kosoku.commonfast.config.safe.xss; import javax.servlet.ServletRequest; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.Charset; public class HttpGetBody { /** * 獲取請求Body * * @param request * @return */ public static String getBodyString(ServletRequest request) { //StringBuilder sb = new StringBuilder(); StringBuffer sb = new StringBuffer(); InputStream inputStream = null; BufferedReader reader = null; try { inputStream = request.getInputStream(); reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8"))); String line = ""; while ((line = reader.readLine()) != null) { sb.append(line); } } catch (IOException e) { e.printStackTrace(); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } return sb.toString(); } }
package org.kosoku.commonfast.config.safe.xss; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; public class XssFilter implements Filter { @Override public void init(FilterConfig config) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { XssHttpServletRequestWrapper xssHttpServletRequestWrapper = new XssHttpServletRequestWrapper((HttpServletRequest) request); chain.doFilter(xssHttpServletRequestWrapper, response); } @Override public void destroy() { } }
package org.kosoku.commonfast.config.safe.xss; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.servlet.Filter; @Configuration public class XSSFilterConfig { @Bean public FilterRegistrationBean filterRegistrationBean() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(xssFilter()); registration.addUrlPatterns("/*"); registration.addInitParameter("paramName", "paramValue"); registration.setName("xssFilter"); return registration; } /** * 創建一個bean * * @return */ @Bean(name = "xssFilter") public Filter xssFilter() { return new XssFilter(); } }
package org.kosoku.commonfast.config.safe.xss; import org.apache.commons.lang.StringUtils; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Iterator; import java.util.Map; public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { HttpServletRequest orgRequest = null; private String body; public XssHttpServletRequestWrapper(HttpServletRequest request) { super(request); orgRequest = request; body = HttpGetBody.getBodyString(request); } /** * 覆蓋getParameter方法,將參數名和參數值都做xss過濾。<br/> * 如果需要獲得原始的值,則通過super.getParameterValues(name)來獲取<br/> * getParameterNames,getParameterValues和getParameterMap也可能需要覆蓋 */ @Override public String getParameter(String name) { String value = super.getParameter(xssEncode(name, 0)); if (null != value) { value = xssEncode(value, 0); } return value; } @Override public String[] getParameterValues(String name) { String[] values = super.getParameterValues(xssEncode(name, 0)); if (values == null) { return null; } int count = values.length; String[] encodedValues = new String[count]; for (int i = 0; i < count; i++) { encodedValues[i] = xssEncode(values[i], 0); } return encodedValues; } @Override public Map getParameterMap() { HashMap paramMap = (HashMap) super.getParameterMap(); paramMap = (HashMap) paramMap.clone(); for (Iterator iterator = paramMap.entrySet().iterator(); iterator.hasNext(); ) { Map.Entry entry = (Map.Entry) iterator.next(); String[] values = (String[]) entry.getValue(); for (int i = 0; i < values.length; i++) { if (values[i] instanceof String) { values[i] = xssEncode(values[i], 0); } } entry.setValue(values); } return paramMap; } @Override public ServletInputStream getInputStream() throws IOException { ServletInputStream inputStream = null; if (StringUtils.isNotEmpty(body)) { body = xssEncode(body, 1); InputStream is = new ByteArrayInputStream(body.getBytes("UTF-8")); inputStream = (ServletInputStream) is; } return inputStream; } /** * 覆蓋getHeader方法,將參數名和參數值都做xss過濾。<br/> * 如果需要獲得原始的值,則通過super.getHeaders(name)來獲取<br/> * getHeaderNames 也可能需要覆蓋 */ @Override public String getHeader(String name) { String value = super.getHeader(xssEncode(name, 0)); if (value != null) { value = xssEncode(value, 0); } return value; } /** * 將容易引起xss漏洞的半角字符直接替換成全角字符 * * @param s * @return */ private static String xssEncode(String s, int type) { if (s == null || s.isEmpty()) { return s; } StringBuilder sb = new StringBuilder(s.length() + 16); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (type == 0) { switch (c) { case '\'': // 全角單引號 sb.append('‘'); break; case '\"': // 全角雙引號 sb.append('“'); break; case '>': // 全角大於號 sb.append('>'); break; case '<': // 全角小於號 sb.append('<'); break; case '&': // 全角&符號 sb.append('&'); break; case '\\': // 全角斜線 sb.append('\'); break; case '#': // 全角井號 sb.append('#'); break; // < 字符的 URL 編碼形式表示的 ASCII 字符(十六進制格式) 是: %3c case '%': processUrlEncoder(sb, s, i); break; default: sb.append(c); break; } } else { switch (c) { case '>': // 全角大於號 sb.append('>'); break; case '<': // 全角小於號 sb.append('<'); break; case '&': // 全角&符號 sb.append('&'); break; case '\\': // 全角斜線 sb.append('\'); break; case '#': // 全角井號 sb.append('#'); break; // < 字符的 URL 編碼形式表示的 ASCII 字符(十六進制格式) 是: %3c case '%': processUrlEncoder(sb, s, i); break; default: sb.append(c); break; } } } return sb.toString(); } public static void processUrlEncoder(StringBuilder sb, String s, int index) { if (s.length() >= index + 2) { // %3c, %3C if (s.charAt(index + 1) == '3' && (s.charAt(index + 2) == 'c' || s.charAt(index + 2) == 'C')) { sb.append('<'); return; } // %3c (0x3c=60) if (s.charAt(index + 1) == '6' && s.charAt(index + 2) == '0') { sb.append('<'); return; } // %3e, %3E if (s.charAt(index + 1) == '3' && (s.charAt(index + 2) == 'e' || s.charAt(index + 2) == 'E')) { sb.append('>'); return; } // %3e (0x3e=62) if (s.charAt(index + 1) == '6' && s.charAt(index + 2) == '2') { sb.append('>'); return; } } sb.append(s.charAt(index)); } /** * 獲取最原始的request * * @return */ public HttpServletRequest getOrgRequest() { return orgRequest; } /** * 獲取最原始的request的靜態方法 * * @return */ public static HttpServletRequest getOrgRequest(HttpServletRequest req) { if (req instanceof XssHttpServletRequestWrapper) { return ((XssHttpServletRequestWrapper) req).getOrgRequest(); } return req; } }