三十四、SpringBoot自定義過濾器
- XSS
跨站指令碼工具(cross 斯特scripting),為不和層疊樣式表(cascading style sheets,CSS)的縮寫混淆,
故將跨站指令碼攻擊縮寫為XSS。惡意攻擊者往web頁面裡插入惡意ScriptScript程式碼,當用戶瀏覽該頁之時,
嵌入其中Web裡面的Script程式碼會被執行,從而達到惡意攻擊使用者的目的。防止XSS攻擊簡單的預防就是對Request
請求中的一些引數去掉一些比較敏感的指令碼命令。原本是打算通過SpringMVC的HandlerInterceptor機制來實現的,通過獲取request然後對request中的引數
進行修改,結果雖然值修改了,但在Controller中獲取的數值還是沒有修改的。沒辦法就是要Filter來完成。
簡單來說就是建立一個新的HttpRequest類XssHttpServletRequestWrapper,然後重寫一些get方法(獲取
引數時對引數進行XSS判斷預防)。
-流程梳理
包裝request->建立過濾器->新增過濾器
- 建立包裝request的類
XssHttpServletRequestWrapper
package cn.lyhxh.config; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.Charset; /** * Created by Ran Han on 2017/12/22. */ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { public XssHttpServletRequestWrapper(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] = cleanXSS(values[i]); } return encodedValues; } @Override public String getParameter(String parameter) { String value = super.getParameter(parameter); if (value != null) { return cleanXSS(value); } return null; } @Override public String getHeader(String name) { String value = super.getHeader(name); if (value == null) return null; return cleanXSS(value); } private static String cleanXSS(String value) { value = value.replaceAll("<", "<").replaceAll(">", ">"); value = value.replaceAll("%3C", "<").replaceAll("%3E", ">"); value = value.replaceAll("\\(", "(").replaceAll("\\)", ")"); value = value.replaceAll("%28", "(").replaceAll("%29", ")"); value = value.replaceAll("'", "'"); value = value.replaceAll("eval\\((.*)\\)", ""); value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\""); value = value.replaceAll("script", ""); return value; } @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream bais = new ByteArrayInputStream(inputHandlers(super.getInputStream ()).getBytes ()); return new ServletInputStream() { @Override public int read() throws IOException { return bais.read(); } @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } }; } 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) { e.printStackTrace(); } finally { if (servletInputStream != null) { try { servletInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } return cleanXSS(sb.toString ()); } }
注意:
getInputStream()方法的流處理,註解方式獲取資料貌似是根據這個流取得的資料。
因為super.getInputStream()流只允許讀取一次,所以在getInputStream()方法中
處理完流資料後返回了一個新的ServletInputStream。另外替換方法裡的替換規則,
也可以根據實際業務需要進行調整。
- 建立過濾器
MyXssFilter
package cn.lyhxh.config; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import java.io.IOException; /** * * 使用註解標註過濾器 * @WebFilter將一個實現了javax.servlet.Filter介面的類定義為過濾器 * 屬性filterName宣告過濾器的名稱,可選 * 屬性urlPatterns指定要過濾 的URL模式,也可使用屬性value來宣告.(指定要過濾的URL模式是必選屬性) * Created by Ran Han on 2017/12/19. */ @WebFilter(filterName="myXssFilter", urlPatterns="/*") public class MyXssFilter implements Filter { FilterConfig filterConfig = null; @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("過濾器初始化"); this.filterConfig = filterConfig; } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("執行過濾操作"); filterChain.doFilter(new XssHttpServletRequestWrapper((HttpServletRequest)servletRequest), servletResponse); } @Override public void destroy() { System.out.println("過濾器銷燬"); this.filterConfig = null; } }
-
新增過濾器
-
方式1(使用註解)
- 在程式入口添加註解
@ServletComponentScan
- 在過濾器上添加註解
@WebFilter(filterName="myXssFilter", urlPatterns="/*")
- 在程式入口添加註解
-
方式2(使用Java Bean)
public class WebInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { servletContext.addListener(RequestContextListener.class); FilterRegistration.Dynamic XssfilterRegistration = servletContext.addFilter("XssSqlFilter",XssFilter.class); XssfilterRegistration.addMappingForUrlPatterns(EnumSet.of( DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE), false, "/*"); } }
由於前面提到的本專案採用的是注入方式,雖然是web專案但並沒有web.xml檔案所以新增過濾器是實現了WebApplicationInitializer類的方法onStartup(),當然也可以新增攔截器。
-