Springboot詳解如何實現(xiàn)SQL注入過濾器過程
場景:以過濾器(Filter)的方式,對所有http請求的入?yún)r截,使用正則表達(dá)式匹配入?yún)⒅械淖址?。存在SQL注入風(fēng)險的參數(shù),中斷請求,并立即返回提示信息。不存在SQL注入風(fēng)險的參數(shù),校驗通過后,放入過濾器鏈,繼續(xù)后續(xù)業(yè)務(wù)。
環(huán)境:本例是基于springboot的web工程,版本:springboot 2.6.3
1.過濾器SqlInjectFilter
SqlInjectFilter,實現(xiàn)javax.servlet.Filter接口。即在doFilter方法中實現(xiàn)具體邏輯。
@Slf4j public class SqlInjectFilter implements Filter { private static final String SQL_REG_EXP = ".*(\\b(select|insert|into|update|delete|from|where|and|or|trancate" + "|drop|execute|like|grant|use|union|order|by)\\b).*"; @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; CustomRequestWrapper requestWrapper = new CustomRequestWrapper(request); Map<String, Object> parameterMap = new HashMap<>(); parameterMap =getParameterMap(parameterMap, request, requestWrapper); // 正則校驗是否有SQL關(guān)鍵字 for (Object obj : parameterMap.entrySet()) { Map.Entry entry = (Map.Entry) obj; Object value = entry.getValue(); if (value != null) { boolean isValid = isSqlInject(value.toString(), servletResponse); if (!isValid) { return; } } } filterChain.doFilter(requestWrapper, servletResponse); } private Map<String, Object> getParameterMap(Map<String, Object> paramMap, HttpServletRequest request, CustomRequestWrapper requestWrapper) { // 1.POST請求獲取參數(shù) if ("POST".equals(request.getMethod().toUpperCase())) { String body = requestWrapper.getBody(); paramMap = JSONObject.parseObject(body, HashMap.class); } else { Map<String, String[]> parameterMap = requestWrapper.getParameterMap(); //普通的GET請求 if (parameterMap != null && parameterMap.size() > 0) { Set<Map.Entry<String, String[]>> entries = parameterMap.entrySet(); for (Map.Entry<String, String[]> next : entries) { paramMap.put(next.getKey(), next.getValue()[0]); } } else { //GET請求,參數(shù)在URL路徑型式,比如server/{var1}/{var2} String afterDecodeUrl = null; try { //編碼過URL需解碼解碼還原字符 afterDecodeUrl = URLDecoder.decode(request.getRequestURI(), "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } paramMap.put("pathVar", afterDecodeUrl); } } return paramMap; } private boolean isSqlInject(String value, ServletResponse servletResponse) throws IOException { if (null != value && value.toLowerCase().matches(SQL_REG_EXP)) { log.info("入?yún)⒅杏蟹欠ㄗ址? " + value); HttpServletResponse response = (HttpServletResponse) servletResponse; Map<String, String> responseMap = new HashMap<>(); // 匹配到非法字符,立即返回 responseMap.put("code", "999"); responseMap.put("message","入?yún)⒅杏蟹欠ㄗ址?); response.setContentType("application/json;charset=UTF-8"); response.setStatus(HttpStatus.OK.value()); response.getWriter().write(JSON.toJSONString(responseMap)); response.getWriter().flush(); response.getWriter().close(); return false; } return true; } }
2.請求裝飾類CustomRequestWrapper
在攔截請求時,會讀取HttpServletRequest的InputStream,而這種數(shù)據(jù)流一旦讀取后,就沒了。那么直接把請求放入過濾器鏈,后續(xù)的環(huán)節(jié)就讀取不到數(shù)據(jù)了。因此,需要一個裝飾類,讀取了InputStream數(shù)據(jù)后,還得回寫到請求中。然后把數(shù)據(jù)完整的裝飾類放入過濾器鏈。這樣攔截了請求,讀取了數(shù)據(jù),并回寫了數(shù)據(jù),數(shù)據(jù)完整性得到保證。
public class CustomRequestWrapper extends HttpServletRequestWrapper { private final String body; public CustomRequestWrapper(HttpServletRequest request) throws IOException { super(request); StringBuilder sb = new StringBuilder(); BufferedReader bufferedReader = null; try { InputStream inputStream = request.getInputStream(); if (inputStream != null) { bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); char[] charBuffer = new char[512]; int bytesRead = -1; while ((bytesRead = bufferedReader.read(charBuffer)) > 0) { sb.append(charBuffer, 0, bytesRead); } } else { sb.append(""); } } catch (IOException e) { e.printStackTrace(); throw e; } finally { if (bufferedReader != null) { try { bufferedReader.close(); } catch (IOException e) { e.printStackTrace(); throw e; } } } body = sb.toString(); } @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream bais = new ByteArrayInputStream(body.getBytes("UTF-8")); return new ServletInputStream() { @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } @Override public int read() { return bais.read(); } }; } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(this.getInputStream(), StandardCharsets.UTF_8)); } public String getBody() { return this.body; } @Override public String getParameter(String name) { return super.getParameter(name); } @Override public Map<String, String[]> getParameterMap() { return super.getParameterMap(); } @Override public Enumeration<String> getParameterNames() { return super.getParameterNames(); } @Override public String[] getParameterValues(String name) { return super.getParameterValues(name); } }
3.過濾器注冊
過濾器生效,需注冊。
@Configuration public class FilterConfiguration { @Bean("sqlFilter") public SqlInjectFilter sqlInjectFilter() { return new SqlInjectFilter(); } @Bean public FilterRegistrationBean<SqlInjectFilter> sqlFilterRegistrationBean() { FilterRegistrationBean<SqlInjectFilter> filterReg = new FilterRegistrationBean<>(); filterReg.setFilter(sqlInjectFilter()); filterReg.addUrlPatterns("/*"); filterReg.setOrder(1); return filterReg; } }
4.測試輔助類
4.1 結(jié)果對象ResultObj
Restful請求返回格式統(tǒng)一。
@Data @NoArgsConstructor @AllArgsConstructor @Builder public class ResultObj { private String code; private String message; }
4.2 Restful的Controller類
SqlInjectionController,包括POST請求和GET請求測試。
@RestController @Slf4j @RequestMapping("/inject") public class SqlInjectionController { @PostMapping("/f1") public Object f1(@RequestBody Object obj) { log.info("SqlInjectionController->f1,接收參數(shù),obj = " + obj.toString()); log.info("SqlInjectionController->f1,返回."); return ResultObj.builder().code("200").message("成功").build(); } @GetMapping("/f2") public Object f2(@RequestParam(name = "var") String var) { log.info("SqlInjectionController->f2,接收參數(shù),var = " + var); log.info("SqlInjectionController->f2,返回."); return ResultObj.builder().code("200").message("成功").build(); } @GetMapping("/f3/{var}") public Object f3(@PathVariable("var") String var) { log.info("SqlInjectionController->f3,接收參數(shù),var = " + var); log.info("SqlInjectionController->f3,返回."); return ResultObj.builder().code("200").message("成功").build(); } }
5.測試
5.1 POST請求測試
URL: http://127.0.0.1:18081/server/inject/f1
入?yún)ⅲ?/p>
{
"userName": "Hangzhou select",
"password": "202206112219"
}
返回:
{
"code": "999",
"message": "入?yún)⒅杏蟹欠ㄗ址?quot;
}
5.2 GET請求測試1
URL: http://127.0.0.1:18081/server/inject/f2?var=56622 INSert
返回:
{
"code": "999",
"message": "入?yún)⒅杏蟹欠ㄗ址?quot;
}
5.3 GET請求測試2
URL: http://127.0.0.1:18081/server/inject/f3/123 delete
返回:
{
"code": "999",
"message": "入?yún)⒅杏蟹欠ㄗ址?quot;
}
到此這篇關(guān)于Springboot詳解如何實現(xiàn)SQL注入過濾器過程的文章就介紹到這了,更多相關(guān)Springboot SQL注入過濾器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java Robot應(yīng)用示例之機(jī)器人功能
這篇文章主要為大家詳細(xì)介紹了Java Robot應(yīng)用示例之機(jī)器人功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-01-01Eclipse 開發(fā)java 出現(xiàn)Failed to create the Java Virtual Machine錯誤
這篇文章主要介紹了Eclipse 開發(fā)java 出現(xiàn)Failed to create the Java Virtual Machine錯誤解決辦法的相關(guān)資料,需要的朋友可以參考下2017-04-04Java數(shù)據(jù)結(jié)構(gòu)之鏈表詳解
本篇文章我們將講解一種新型的數(shù)據(jù)結(jié)構(gòu)—鏈表,鏈表是一種使用廣泛的通用數(shù)據(jù)結(jié)構(gòu),它可以用來作為實現(xiàn)棧,隊列等數(shù)據(jù)結(jié)構(gòu)的基礎(chǔ).文中有非常詳細(xì)的介紹,需要的朋友可以參考下2021-05-05Java向數(shù)據(jù)庫插入中文出現(xiàn)亂碼解決方案
這篇文章主要介紹了Java向數(shù)據(jù)庫插入中文出現(xiàn)亂碼解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-08-08SpringBoot3?Web編程開發(fā)的工程搭建攔截器及測試工具示例
這篇文章主要介紹了SpringBoot3?Web編程開發(fā)的工程搭建攔截器及測試工具示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08springsecurity中http.permitall與web.ignoring的區(qū)別說明
這篇文章主要介紹了springsecurity中http.permitall與web.ignoring的區(qū)別說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08