SpringBoot通過(guò)Filter實(shí)現(xiàn)整個(gè)項(xiàng)目接口的SQL注入攔截詳解
前言
業(yè)務(wù)背景:一般情況下,我們的項(xiàng)目是需要做SQL注入、XSS攔截等安全性問(wèn)題,本來(lái)是打算在網(wǎng)關(guān)層面做全局過(guò)濾器或者自定義過(guò)濾器,但是苦于遇到依賴問(wèn)題等實(shí)在短期內(nèi)無(wú)法解決,于是便在其下一層針對(duì)某個(gè)項(xiàng)目做SQL注入攔截。
一、直接上源碼
1.啟動(dòng)類路徑下,直接建一個(gè)filter包,
過(guò)濾器SqlInjectFilter
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.bc.base.log.BaseLog; import com.bc.digitalmanage.entity.common.ResultMsg; import org.apache.commons.lang3.StringUtils; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpStatus; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.*; /** * @author w7h1te-ywq * @version 9月... * @date 2022/9/13 16:00 */ @WebFilter(urlPatterns = "/",filterName = "SqlInjectionFilter") @Configuration public class SqlInjectionFilter implements Filter { private static BaseLog log = BaseLog.getInstance(SqlInjectionFilter.class.getName()); private static final Set<String> ALLOWED_PATHS = Collections.unmodifiableSet(new HashSet<>( Arrays.asList(""))); private static final String SQL_REG_EXP = ".*(\\b(select|insert|into|update|delete|from|where|trancate" + "|drop|execute|grant|use|union)\\b).*"; @Override public void init(FilterConfig filterConfig) throws ServletException { } @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<>(); String path = request.getRequestURI().substring(request.getContextPath().length()).replaceAll("[/]+$", ""); boolean allowedPath = ALLOWED_PATHS.contains(path); if (!allowedPath) { parameterMap = getParameterMap(parameterMap, request, requestWrapper); // 正則校驗(yàn)是否有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請(qǐng)求獲取參數(shù) if ("POST".equals(request.getMethod().toUpperCase())) { String body = requestWrapper.getBody(); if(StringUtils.isNotEmpty(body)){ boolean jsonType = getJSONType(body); if(jsonType==true){ paramMap = JSONObject.parseObject(body, HashMap.class); }else { String[] split = body.split("&"); for (int i = 0; i < split.length; i++) { String[] split1; split1 = split[i].split("="); paramMap.put(split1[0],split1[1]); split1 = null; } } }else { Map<String, String[]> parameterMap = requestWrapper.getParameterMap(); 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 { Map<String, String[]> parameterMap = requestWrapper.getParameterMap(); //普通的GET請(qǐng)求 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請(qǐng)求,參數(shù)在URL路徑型式,比如server/{var1}/{var2} String afterDecodeUrl = null; try { //編碼過(guò)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 { ResultMsg<Object> resultMsg = new ResultMsg<>(); if (null != value && value.toLowerCase().matches(SQL_REG_EXP)) { log.info(" SqlInjectionFilter isSqlInject :入?yún)⒅杏蟹欠ㄗ址? " + value); HttpServletResponse response = (HttpServletResponse) servletResponse; // Map<String, String> responseMap = new HashMap<>(); // 匹配到非法字符,立即返回 // responseMap.put("code", "999"); // responseMap.put("message","入?yún)⒅杏蟹欠ㄗ址?); resultMsg.setResultMsg("輸入數(shù)據(jù)中存在非法字符!"); resultMsg.setSuccess(false); resultMsg.setResultCode("999"); resultMsg.setData("入?yún)⒅杏蟹欠ㄗ址猄QL注入攔截?。?!"); response.setContentType("application/json;charset=UTF-8"); response.setStatus(HttpStatus.OK.value()); response.getWriter().write(JSON.toJSONString(resultMsg)); response.getWriter().flush(); response.getWriter().close(); return false; } return true; } private boolean getJSONType(String str){ boolean result = false; if (StringUtils.isNotBlank(str)) { str = str.trim(); if (str.startsWith("{") && str.endsWith("}")) { result = true; } else if (str.startsWith("[") && str.endsWith("]")) { result = true; } } return result; } @Override public void destroy() { } }
設(shè)置請(qǐng)求裝飾類CustomRequestWrapper
import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.*; import java.nio.charset.StandardCharsets; import java.util.Enumeration; import java.util.Map; /** * @author w7h1te-ywq * @version 9月... * @date 2022/9/13 16:01 */ 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); } }
以bean注解來(lái)詮釋次配置文件相關(guān)代碼FilterConfiguration
import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.*; import java.nio.charset.StandardCharsets; import java.util.Enumeration; import java.util.Map; /** * @author w7h1te-ywq * @version 9月... * @date 2022/9/13 16:01 */ 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); } }
二、在springboot啟動(dòng)類上添加注解聲明過(guò)濾器
代碼:
@ServletComponentScan(basePackages = “com.bc.***.filter”)
三、什么是sql注入
SQL注入是比較常見(jiàn)的網(wǎng)絡(luò)攻擊方式之一,在客戶端在向服務(wù)器發(fā)送請(qǐng)求的時(shí)候,sql命令通過(guò)表單提交或者url字符串拼接傳遞到后臺(tái)持久層,最終達(dá)到欺騙服務(wù)器執(zhí)行惡意的SQL命令;
它不是利用操作系統(tǒng)的BUG來(lái)實(shí)現(xiàn)攻擊,而是針對(duì)程序員編程時(shí)的疏忽,通過(guò)SQL語(yǔ)句,實(shí)現(xiàn)無(wú)帳號(hào)登錄,甚至篡改數(shù)據(jù)庫(kù)。
sql注入可能產(chǎn)生的影響
惡意用戶可以未經(jīng)授權(quán)訪問(wèn)您的應(yīng)用程序并竊取數(shù)據(jù)。
他們可以更改,刪除數(shù)據(jù)庫(kù)中的數(shù)據(jù)并關(guān)閉您的應(yīng)用程序。
黑客還可以通過(guò)執(zhí)行數(shù)據(jù)庫(kù)特定的系統(tǒng)命令來(lái)控制運(yùn)行數(shù)據(jù)庫(kù)服務(wù)器的系統(tǒng)。
到此這篇關(guān)于SpringBoot通過(guò)Filter實(shí)現(xiàn)整個(gè)項(xiàng)目接口的SQL注入攔截詳解的文章就介紹到這了,更多相關(guān)SpringBoot的SQL注入攔截內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot頂層接口實(shí)現(xiàn)類注入項(xiàng)目的方法示例
- Springboot根據(jù)配置文件動(dòng)態(tài)注入接口實(shí)現(xiàn)類詳解
- 使用SpringBoot根據(jù)配置注入接口的不同實(shí)現(xiàn)類(代碼演示)
- springboot接口多實(shí)現(xiàn)類選擇性注入解決方案
- SpringBoot使用@Autowired為多實(shí)現(xiàn)的接口注入依賴
- 使用Springboot根據(jù)配置文件動(dòng)態(tài)注入接口實(shí)現(xiàn)類
- SpringBoot中多個(gè)實(shí)現(xiàn)的接口正確注入的六種方式
相關(guān)文章
Mybatis動(dòng)態(tài)SQL之if、choose、where、set、trim、foreach標(biāo)記實(shí)例詳解
動(dòng)態(tài)SQL就是動(dòng)態(tài)的生成SQL。接下來(lái)通過(guò)本文給大家介紹Mybatis動(dòng)態(tài)SQL之if、choose、where、set、trim、foreach標(biāo)記實(shí)例詳解的相關(guān)知識(shí),感興趣的朋友一起看看吧2016-09-09Spring框架中@AliasFor注解詳細(xì)說(shuō)明
這篇文章主要給大家介紹了關(guān)于Spring框架中@AliasFor注解詳細(xì)說(shuō)明的相關(guān)資料,@AliasFor是Spring Framework中的一個(gè)注解,它用于指定注解屬性之間的別名關(guān)系,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-02-02ssm項(xiàng)目改造spring?boot項(xiàng)目完整步驟
Spring?Boot現(xiàn)在已經(jīng)成為Java開(kāi)發(fā)領(lǐng)域的一顆璀璨明珠,它本身是包容萬(wàn)象的,可以跟各種技術(shù)集成,下面這篇文章主要給大家介紹了關(guān)于ssm項(xiàng)目改造spring?boot項(xiàng)目的相關(guān)資料,需要的朋友可以參考下2023-04-04詳解java并發(fā)編程(2) --Synchronized與Volatile區(qū)別
這篇文章主要介紹了Synchronized與Volatile區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04基于synchronized修飾靜態(tài)和非靜態(tài)方法
這篇文章主要介紹了基于synchronized修飾靜態(tài)和非靜態(tài)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04Java之SpringCloud Eurka注冊(cè)錯(cuò)誤解決方案
這篇文章主要介紹了Java之SpringCloud Eurka注冊(cè)錯(cuò)誤解決方案,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07