Java使用過濾器防止SQL注入XSS指令碼注入的實現
阿新 • • 發佈:2021-01-13
前幾天有個客戶在系統上寫了一段html語句,開啟頁面就顯示一張炒雞大的圖片,影響美觀。後來仔細想想,幸虧注入的僅僅是html語句,知道嚴重性後,馬上開始一番系統安全配置。
一. 定義過濾器
package com.cn.unit.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang.StringUtils; import org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.multipart.commons.CommonsMultipartResolver; /** * 過濾器 * Created by adonis on 2020/12/12 */ public class SafeFilter implements Filter{ // 配置資訊物件 public FilterConfig filterConfig; /** * 初始化 * 與我們編寫的Servlet程式一樣,Filter的建立和銷燬由WEB伺服器負責。 * Web應用程式啟動時,Web伺服器將建立Filter的例項物件,並呼叫其init方法,讀取web.xml配置, * 完成物件的初始化功能,從而為後續的使用者請求作好攔截的準備工作。 * Filter物件只會建立一次,init方法也只會執行一次。 * 開發人員通過init方法的引數,可獲得代表當前Filter配置資訊的FilterConfig物件。 */ @Override public void init(FilterConfig filterConfig) throws ServletException { filterConfig = config; } /** * 攔截請求 * 這個方法完成實際的過濾操作。當客戶請求訪問與過濾器關聯的URL的時候,Servlet過濾器將先執行doFilter方法。 * FilterChain引數用於訪問後續過濾器。 */ @Override public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain) throws IOException,ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; String enctype = httpRequest.getContentType(); if(StringUtils.isNotBlank(enctype) && enctype.contains("multipart/form-data")){ // 上傳檔案 CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver( httpRequest.getSession().getServletContext()); MultipartHttpServletRequest multipartRequest = commonsMultipartResolver.resolveMultipart(httpRequest); XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(multipartRequest); chain.doFilter(xssRequest,response); }else{ // 普通表單和Ajax XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request); chain.doFilter(xssRequest,response); } } /** * 銷燬 * Filter物件建立後會駐留在記憶體,當Web應用移除或伺服器停止時才銷燬。在Web容器解除安裝Filter物件之前被呼叫。 * 該方法在Filter的生命週期中僅執行一次。在這個方法中,可以釋放過濾器使用的資源。 */ @Override public void destroy() { this.filterConfig = null; } }
二. 過濾包裝器,實現引數值過濾
package com.cn.unit.filter; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; /** * 使用者請求包裝類 * Created by adonis on 2020/12/12 */ public class SafeHttpServletRequestWrapper extends HttpServletRequestWrapper{ public SafeHttpServletRequestWrapper(HttpServletRequest request) { super(request); } @Override public String getParameter(String name) { String value = super.getParameter(name); if (value != null) { value = xssEncode(value); } return value; } @Override public String[] getParameterValues(String name) { String[] value = super.getParameterValues(name); if(value != null){ for (int i = 0; i < value.length; i++) { value[i] = xssEncode(value[i]); } } return value; } @Override public Map getParameterMap() { return super.getParameterMap(); } /** * 請求頭不過濾 */ @Override public String getHeader(String name) { return super.getHeader(name); } /** * 將容易引起注入的關鍵字的半形字元直接替換成全形字元 * @param value 過濾前的值 * @return 過濾後的值 */ private static String xssEncode(String value) { if (value == null || value.isEmpty()) { return value; } // 防SQL注入轉義 value = StringEscapeUtils.escapeSql(value); // HTML防注入,個人建議使用第三種 // 1.防HTML注入轉義(HtmlUtils工具類,漢字不轉義,雙引號轉義,存在JSON封裝需要反轉義) value = HtmlUtils.htmlEscape(value); /* // 2.防HTML注入轉義(StringEscapeUtils工具類,漢字也轉義,取出時需要反轉義) // value = StringEscapeUtils.escapeHtml(value); // 3.字串替換法(通過各種迴圈替換字串測試,最終還是replace替換效果最佳) value = value.replaceAll("<","<"); value = value.replaceAll(">",">"); value = value.replaceAll("'","'"); value = value.replaceAll(";","﹔"); value = value.replaceAll("&","&"); value = value.replaceAll("%","﹪"); value = value.replaceAll("#","#"); value = value.replaceAll("select","seleᴄt");// "c"→"ᴄ" value = value.replaceAll("truncate","trunᴄate");// "c"→"ᴄ" value = value.replaceAll("exec","exeᴄ");// "c"→"ᴄ" value = value.replaceAll("join","jᴏin");// "o"→"ᴏ" value = value.replaceAll("union","uniᴏn");// "o"→"ᴏ" value = value.replaceAll("drop","drᴏp");// "o"→"ᴏ" value = value.replaceAll("count","cᴏunt");// "o"→"ᴏ" value = value.replaceAll("insert","ins℮rt");// "e"→"℮" value = value.replaceAll("update","updat℮");// "e"→"℮" value = value.replaceAll("delete","delet℮");// "e"→"℮" value = value.replaceAll("script","sᴄript");// "c"→"ᴄ" value = value.replaceAll("cookie","cᴏᴏkie");// "o"→"ᴏ" value = value.replaceAll("iframe","ifram℮");// "e"→"℮" value = value.replaceAll("onmouseover","onmouseov℮r");// "e"→"℮" value = value.replaceAll("onmousemove","onmousemov℮");// "e"→"℮"*/ return value; } }
三. 配置web.xml新增過濾器
<!-- 配置過濾器防止SQL注入XSS注入 --> <filter> <filter-name>XssSqlFilter</filter-name> <filter-class>com.cn.unit.filter.SafeFilter</filter-class> </filter> <filter-mapping> <filter-name>XssSqlFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
配置各節點簡單介紹:
節點名 | 介紹 |
---|---|
<filter> | 指定一個過濾器 |
<filter-name> | 用於為過濾器指定一個名字,該元素的內容不能為空 |
<filter-class> | 指定過濾器的完整的限定類名 |
<init-param> | 為過濾器指定初始化引數。在過濾器中,可以使用FilterConfig介面物件來訪問初始化引數 |
<param-name> | <init-param>的子元素,指定引數的名字 |
<param-value> | <init-param>的子元素,指定引數的值 |
<filter-mapping> | 設定一個Filter所負責攔截的資源。可通過Servlet名稱或資源訪問的請求路徑指定 |
<filter-name> | 子元素用於設定filter的註冊名稱。該值必須是在<filter>元素中宣告過的過濾器的名字 |
<url-pattern> | 設定 filter 所攔截的請求路徑(過濾器關聯的URL樣式) |
<servlet-name> | 指定過濾器所攔截的Servlet名稱 |
<dispatcher> | 指定過濾器所攔截的資源被 Servlet 容器呼叫的方式,預設REQUEST |
四. 靜態資源跳過過濾
在實際開發的過程中,js、css等靜態資源也進行過濾,消耗伺服器效能,因此把一些不必要過濾的直接跳過過濾器,實現如下:
4.1 在web.xml配置檔案中新增引數,儲存靜態資源所在的路徑
<init-param> <param-name>excludeFilter</param-name><!-- 靜態資源不進行過濾,如js、css檔案 --> <param-value>/document/;/ligentres/</param-value> </init-param>
如圖:
4.2 過濾器初始化方法,讀取靜態資源所在的路徑
public FilterConfig filterConfig; public String[] excludeFilterArray; @Override public void init(FilterConfig config) throws ServletException { filterConfig = config; // 讀取web配置檔案中的靜態資源所在路徑 String excludeFilter = filterConfig.getInitParameter("excludeFilter"); excludeFilterArray = excludeFilter.split(";"); }
4.3 過濾器攔截請求,若是靜態資源所在的路徑直接跳過過濾器
@Override public void doFilter(ServletRequest request,ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; String uri = httpRequest.getRequestURI(); // 靜態資源直接跳過,不進行過濾 if(uri==null||this.isContains(uri,excludeFilterArray)) { chain.doFilter(request,response); return; } ...... } // 判斷陣列是否包含某一元素 public boolean isContains(String uri,String[] regx) { boolean result = false; for (int i = 0; i < regx.length; i++) { if (uri.indexOf(regx[i]) != -1) { return true; } } return result; }
五. 大功告成
借鑑前人的經驗,一開始測試的時候發現,普通的表單提交和ajax提交可以過濾其引數,但上傳檔案時就無法進入過濾了。
後來經過研究,用於處理檔案上傳的 MultipartResolver ,當收到請求時,DispatcherServlet 的 checkMultipart() 方法會呼叫 MultipartResolver 的 isMultipart() 方法判斷請求中是否包含檔案。如果請求資料中包含檔案,則呼叫 MultipartResolver 的 resolveMultipart() 方法對請求的資料進行解析,然後將檔案資料解析成 MultipartFile 並封裝在 MultipartHttpServletRequest 物件中,最後傳遞給 Controller。因此我們只需要在定義過濾器時,先獲取請求頭判斷是否為檔案上傳,若是再對資料進行解析便可。
到此這篇關於Java使用過濾器防止SQL注入XSS指令碼注入的實現的文章就介紹到這了,更多相關Java 過濾器防止SQL注入 內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!