【SpringBoot】基于Filter實現SQL注入過濾器

最近掃出了一個SQL注入安全漏洞,用戶的非法輸入可能導致數據泄露、數據篡改甚至系統崩潰,為了有效防范 SQL 注入攻擊,除了在代碼層面使用參數化查詢和預編譯語句外,還可以通過實現一個Filter來過濾掉潛在的危險輸入。本文將介紹如何基于Filter接口實現SQL 注入過濾器

SQL注入攔截的原理很簡單,用請求參數匹配SQL關鍵字,匹配上了就說明請求存在非法字符不予放行

SqlLnjectionFilter SQL注入過濾器

package com.largescreen.common.filter;import com.fasterxml.jackson.databind.ObjectMapper;
import com.largescreen.common.enums.HttpMethod;
import com.largescreen.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.util.AntPathMatcher;import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** sql注入過濾器*/
@Slf4j
public class SqlLnjectionFilter implements Filter {/*** 白名單*/public List<String> excludes = new ArrayList<>();private static final AntPathMatcher matcher = new AntPathMatcher();private static final String badStrReg = "\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";/*** 整體都忽略大小寫*/private static final Pattern sqlPattern = Pattern.compile(badStrReg, Pattern.CASE_INSENSITIVE);@Overridepublic void init(FilterConfig filterConfig) throws ServletException {String tempExcludes = filterConfig.getInitParameter("excludes");if (StringUtils.isNotEmpty(tempExcludes)){String[] urls = tempExcludes.split(",");for (String url : urls){excludes.add(url);}}}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse response = (HttpServletResponse) servletResponse;String path = request.getServletPath();if(matchAny(excludes,path)){filterChain.doFilter(request, response);return;}// 從request中獲取當前請求中所有的參數名稱String sql = StringUtils.EMPTY;Map<String, String[]> parameterMap = request.getParameterMap();for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {String[] values = entry.getValue();for (int i = 0; i < values.length; i++) {sql += values[i];}}if (sqlValidate(sql)) {errorResp(response);} else {// 校驗post請求String contentType = request.getContentType();Boolean existSql = false;if (HttpMethod.POST.matches(request.getMethod())) {BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));String bodyString = IOUtils.toString(reader);if(StringUtils.isNotBlank(bodyString)){if(StringUtils.startsWithIgnoreCase(contentType, MediaType.APPLICATION_JSON_VALUE)){existSql = sqlValidate(bodyString);}else if (StringUtils.startsWithIgnoreCase(contentType, MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {existSql = sqlValidate(bodyString);} else if (StringUtils.startsWithIgnoreCase(contentType, MediaType.MULTIPART_FORM_DATA_VALUE)) {existSql = sqlValidate(bodyString);}}//  如果存在sql注入,直接攔截請求if (existSql) {errorResp(response);return;};}}filterChain.doFilter(request, response);}@Overridepublic void destroy() {}/*** 非法請求響應* @param response* @throws IOException*/private void errorResp(HttpServletResponse response) throws IOException {response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());response.setContentType("application/json; charset=utf-8");response.setCharacterEncoding("UTF-8");Map result = new HashMap();result.put("code", HttpStatus.INTERNAL_SERVER_ERROR.value());result.put("msg","非法請求");ObjectMapper mapper = new ObjectMapper();String str = mapper.writeValueAsString(result);ServletOutputStream outputStream = response.getOutputStream();outputStream.write(new String(str.getBytes(),"utf-8").getBytes());outputStream.flush();}/*** 判斷輸入的字符串是否包含SQL注入** @param str 輸入的字符串* @return 如果輸入的字符串包含SQL注入,返回 true,否則返回 false。*/public static boolean sqlValidate(String str) {str = str.toLowerCase();Matcher matcher = sqlPattern.matcher(str);if (matcher.find()) {log.error("SqlInjectionFilter 參數[{}]中包含不允許sql的關鍵詞", str);return true;}return false;}/*** 匹配多個路徑** @param patterns 路徑模式列表* @param path     需要匹配的實際路徑* @return 匹配的路徑模式(如果匹配成功),否則返回 null*/public static boolean matchAny(List<String> patterns, String path) {for (String pattern : patterns) {if (matcher.match(pattern, path)) {return true;}}return false;}}

上面的過濾器除了會校驗請求地址中拼接的參數,還會校驗post請求體中的參數,但直接讀請求體的數據是有問題的,http請求的數據流只能讀取一次,在過濾器中讀取請求體后serlvlet就會拿不到數據,所以得增強request請求確保數據能重復讀取

RepeatedlyRequestWrapper? request可重復讀實現類

package com.largescreen.common.filter;import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import com.largescreen.common.utils.http.HttpHelper;
import com.largescreen.common.constant.Constants;/*** 構建可重復讀取inputStream的request* */
public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper
{private final byte[] body;public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException{super(request);request.setCharacterEncoding(Constants.UTF8);response.setCharacterEncoding(Constants.UTF8);body = HttpHelper.getBodyString(request).getBytes(Constants.UTF8);}@Overridepublic BufferedReader getReader() throws IOException{return new BufferedReader(new InputStreamReader(getInputStream()));}@Overridepublic ServletInputStream getInputStream() throws IOException{final ByteArrayInputStream bais = new ByteArrayInputStream(body);return new ServletInputStream(){@Overridepublic int read() throws IOException{return bais.read();}@Overridepublic int available() throws IOException{return body.length;}@Overridepublic boolean isFinished(){return false;}@Overridepublic boolean isReady(){return false;}@Overridepublic void setReadListener(ReadListener readListener){}};}
}

添加一個新的過濾器RepeatableFilter,在doFilter方法中對request進行增強,只要這個過濾器執行順序足夠靠前,后續的過濾器就都能重復讀參數了

RepeatableFilter 可重復讀過濾器

package com.largescreen.common.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 com.largescreen.common.enums.HttpMethod;
import com.largescreen.common.utils.StringUtils;
import org.springframework.http.MediaType;/*** Repeatable 過濾器* */
public class RepeatableFilter implements Filter
{@Overridepublic void init(FilterConfig filterConfig) throws ServletException{}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException{ServletRequest requestWrapper = null;if (request instanceof HttpServletRequest&& HttpMethod.POST.matches(((HttpServletRequest) request).getMethod())){requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response);}if (null == requestWrapper){chain.doFilter(request, response);}else{chain.doFilter(requestWrapper, response);}}@Overridepublic void destroy(){}
}

將上面兩個過濾器注冊到過濾鏈中

FilterConfig 過濾器配置

package com.largescreen.framework.config;import java.util.HashMap;
import java.util.Map;
import javax.servlet.DispatcherType;import com.largescreen.common.filter.SqlLnjectionFilter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.largescreen.common.filter.RepeatableFilter;
import com.largescreen.common.filter.XssFilter;
import com.largescreen.common.utils.StringUtils;/*** Filter配置**/
@Configuration
public class FilterConfig
{@Value("${sql.excludes}")private String sqlExcludes;@Value("${sql.urlPatterns}")private String sqlUrlPatterns;@Bean@ConditionalOnProperty(value = "sql.enabled", havingValue = "true")public FilterRegistrationBean sqlFilterRegistration(){FilterRegistrationBean registration = new FilterRegistrationBean();registration.setDispatcherTypes(DispatcherType.REQUEST);registration.setFilter(new SqlLnjectionFilter());registration.addUrlPatterns(StringUtils.split(sqlUrlPatterns, ","));registration.setName("sqlFilter");registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE + 10);Map<String, String> initParameters = new HashMap<>();initParameters.put("excludes", sqlExcludes);registration.setInitParameters(initParameters);return registration;}@SuppressWarnings({ "rawtypes", "unchecked" })@Beanpublic FilterRegistrationBean someFilterRegistration(){FilterRegistrationBean registration = new FilterRegistrationBean();registration.setFilter(new RepeatableFilter());registration.addUrlPatterns("/*");registration.setName("repeatableFilter");registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);return registration;}}

在配置文件中加入SQL過濾的白名單等信息

application.yml

# 防止sql注入
sql:# 過濾開關enabled: true# 排除鏈接(多個用逗號分隔)excludes: /system/*# 匹配鏈接urlPatterns: /*

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/901918.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/901918.shtml
英文地址,請注明出處:http://en.pswp.cn/news/901918.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Spring Boot 項目里設置默認國區時區,Jave中Date時區配置

在 Spring Boot 項目里設置國區時區&#xff08;也就是中國標準時間&#xff0c;即 Asia/Shanghai&#xff09;&#xff0c;可通過以下幾種方式實現&#xff1a; 方式一&#xff1a;在application.properties或application.yml里設置 application.properties properties sp…

Python環境中在線訓練機器學習模型所遇到的問題及解決方案

我最近開發個智能控制系統,包括實時數據采集、預測、策略優化等功能,最近增加在線學習功能,也就是在線進行模型訓練,在線進行模型訓練時出現了問題,現象為: 控制臺報: cmdstanpy - INFO - Chain [1] start processing所有任務、線程停止,Web服務登錄無法訪問后臺的pyt…

【教程】無視硬件限制強制升級Windows 11

轉載請注明出處&#xff1a;小鋒學長生活大爆炸[xfxuezhagn.cn] 如果本文幫助到了你&#xff0c;歡迎[點贊、收藏、關注]哦~ 1、下載升級工具&#xff1a;https://github.com/builtbybel/Flyby11/releases 2、解壓后打開軟件&#xff1a; 3、拖入win11.iso或者自動下載&#xf…

麥科信汽車診斷示波器在機車維修領域中的應用實例

麥科信汽車診斷示波器在機車維修領域中的應用實例 “Micsig SATO1004的錯誤幀統計功能與歷史波形存儲&#xff0c;讓我們在診斷間歇性CAN故障時有了決定性武器。這不僅是工具升級&#xff0c;更是維修理念的革新。” — Ian Coffey, Mototek技術總監&#xff08;歐洲ECU診…

【IDEA2020】 解決開發時遇到的一些問題

目錄 一、批量更新數據庫數據 逐條更新 Db.updateEntitiesBatch() 二、Error running&#xff0c;Command line is too long. Shorten command line 報錯場景 報錯分析 解決方法 一、批量更新數據庫數據 逐條更新 List<UserModel> ums userMapper.selectListBy…

算法01-最小生成樹prim算法

最小生成樹prim算法 題源&#xff1a;代碼隨想錄卡哥的題 鏈接&#xff1a;https://kamacoder.com/problempage.php?pid1053 時間&#xff1a;2025-04-18 難度&#xff1a;4? 題目&#xff1a; 1. 題目描述&#xff1a; 在世界的某個區域&#xff0c;有一些分散的神秘島嶼&…

cpolar 內網穿透 實現公網可以訪問本機

1、登錄網站&#xff0c;升級成專業版&#xff0c;測試的話建議選一個月付費&#xff0c;選擇預留 2、保留的TCP地址增加一條記錄&#xff0c;描述可以自己取 3、驗證&#xff0c;生成一個Authtocken碼 4、在安裝目錄下&#xff0c;打開CMD命令&#xff0c;復制上面的碼運行aut…

c#內存泄露的原因和解決辦法

內存泄漏的原因 不正確的對象引用&#xff1a;最常見的原因是對象不再需要時未被垃圾回收器回收。例如&#xff0c;如果一個對象被一個不再使用的變量引用&#xff0c;它將不會被垃圾回收。事件訂閱者未取消&#xff1a;如果訂閱了一個事件但沒有在對象不再需要時取消訂閱&…

TDengine Restful 接口API

簡介 為支持各種不同類型平臺的開發&#xff0c;TDengine 提供符合 RESTful 設計標準的 API&#xff0c;即 REST API。為最大程度降低學習成本&#xff0c;不同于其他數據庫 REST API 的設計方法&#xff0c;TDengine 直接通過 HTTP POST 請求 BODY 中包含的 SQL 語句來操作數…

【Contiki】Contiki process概述

00. 目錄 文章目錄 00. 目錄01. 進程類型02. 進程結構03. 事件04. 進程調度函數05. 程序實例06. process實現07. 附錄 01. 進程類型 進程類型主要有**協同式&#xff08;cooperative&#xff09;和搶占式&#xff08;preemptive&#xff09;**兩種。 協同式進程&#xff0c;要…

哪種電腦更穩定?Mac?Windows?還是云電腦? 實測解密

隨著科技的發展進步&#xff0c;電腦已成為當下各類群體的必備產品之一&#xff0c;它的妙用有很多&#xff0c;無論是學生黨、打工人還是已經退休的人群或都離不開它的存在。然而&#xff0c;電腦雖好卻也差異很大、不同品牌、不同系統、不同配置、不同價位的統統都會有區別。…

華為openEuler操作系統全解析:起源、特性與生態對比

華為openEuler操作系統全解析&#xff1a;起源、特性與生態對比 一、起源與發展歷程 openEuler&#xff08;歐拉操作系統&#xff09;是華為于2019年開源的Linux發行版&#xff0c;其前身為華為內部研發的服務器操作系統EulerOS。EulerOS自2010年起逐步發展&#xff0c;支持華…

第 7 期:DDPM 采樣提速方案:從 DDPM 到 DDIM

本期關鍵詞:采樣加速、DDIM 推導、可控性提升、偽逆過程、代碼實戰 前情回顧:DDPM 的采樣瓶頸 在前幾期中,我們構建了一個完整的 DDPM 生成流程。但是你可能已經發現: 生成一張圖像太慢了!!! 原因是: DDPM 要在 T 個時間步中一步步地去噪,從 x_T → x_0。而通常 T 至…

chrome中的copy xpath 與copy full xpath的區別

學過測試或者爬蟲的&#xff0c;都感覺獲取網頁元素&#xff0c;使用xpath最方便 但其中有一些細節可能會使你摸不清頭腦 比如有時候copy xpath會定位不準確&#xff0c;而使用copy full xpath就可以定位 1、copy xpath&#xff08;相對路徑定位&#xff09; 優點&#xff…

學習海康VisionMaster之中線查找

一&#xff1a;進一步學習了 今天學習下VisionMaster中的中線查找&#xff0c;這個就是字面意思&#xff0c;輸入兩條直線&#xff0c;輸出兩條直線的中線 二&#xff1a;開始學習 1&#xff1a;什么是中線查找&#xff1f;今天這個比較簡單&#xff0c;其實這個模塊算是一個幾…

深入淺出 Multi-Head Attention:原理 + 例子 + PyTorch 實現

本文帶你一步步理解 Transformer 中最核心的模塊&#xff1a;多頭注意力機制&#xff08;Multi-Head Attention&#xff09;。從原理到實現&#xff0c;配圖 舉例 PyTorch 代碼&#xff0c;一次性說清楚&#xff01; 什么是 Multi-Head Attention&#xff1f; 簡單說&#x…

常用 Git 命令詳解

Git 是一個強大的版本控制工具&#xff0c;廣泛用于軟件開發和團隊協作中。掌握 Git 命令可以幫助開發者更高效地管理代碼版本和項目進度。本文將介紹一些常用的 Git 命令&#xff0c;并提供示例以幫助你更好地理解和應用這些命令。 目錄 常用命令 git clonegit stashgit pul…

NO.96十六屆藍橋杯備戰|圖論基礎-多源最短路|Floyd|Clear And Present Danger|災后重建|無向圖的最小環問題(C++)

多源最短路&#xff1a;即圖中每對頂點間的最短路徑 floyd算法本質是動態規劃&#xff0c;?來求任意兩個結點之間的最短路&#xff0c;也稱插點法。通過不斷在兩點之間加?新的點&#xff0c;來更新最短路。 適?于任何圖&#xff0c;不管有向?向&#xff0c;邊權正負&…

電流模式控制學習

電流模式控制 電流模式控制&#xff08;CMC&#xff09;是開關電源中廣泛使用的一種控制策略&#xff0c;其核心思想是通過內環電流反饋和外環電壓反饋共同調節占空比。相比電壓模式控制&#xff0c;CMC具有更快的動態響應和更好的穩定性&#xff0c;但也存在一些固有缺點。 …

MATLAB 控制系統設計與仿真 - 36

魯棒工具箱定義了個新的對象類ureal,可以定義在某個區間內可變的變量。 函數的調用格式為&#xff1a; p ureal(name,nominalvalue) % name為變量名,nominalValue為標稱值&#xff0c;默認變化值為/-1 p ureal(name,nominalvalue,PlusMinus,plusminus) p ureal(name,nomin…