SpringBoot通過Filter實(shí)現(xiàn)整個(gè)項(xiàng)目接口的SQL注入攔截詳解
前言
業(yè)務(wù)背景:一般情況下,我們的項(xiàng)目是需要做SQL注入、XSS攔截等安全性問題,本來是打算在網(wǎng)關(guān)層面做全局過濾器或者自定義過濾器,但是苦于遇到依賴問題等實(shí)在短期內(nèi)無法解決,于是便在其下一層針對(duì)某個(gè)項(xiàng)目做SQL注入攔截。
一、直接上源碼
1.啟動(dòng)類路徑下,直接建一個(gè)filter包,
過濾器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 {
//編碼過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注解來詮釋次配置文件相關(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)類上添加注解聲明過濾器
代碼:
@ServletComponentScan(basePackages = “com.bc.***.filter”)
三、什么是sql注入
SQL注入是比較常見的網(wǎng)絡(luò)攻擊方式之一,在客戶端在向服務(wù)器發(fā)送請(qǐng)求的時(shí)候,sql命令通過表單提交或者url字符串拼接傳遞到后臺(tái)持久層,最終達(dá)到欺騙服務(wù)器執(zhí)行惡意的SQL命令;
它不是利用操作系統(tǒng)的BUG來實(shí)現(xiàn)攻擊,而是針對(duì)程序員編程時(shí)的疏忽,通過SQL語句,實(shí)現(xiàn)無帳號(hào)登錄,甚至篡改數(shù)據(jù)庫。
sql注入可能產(chǎn)生的影響
惡意用戶可以未經(jīng)授權(quán)訪問您的應(yīng)用程序并竊取數(shù)據(jù)。
他們可以更改,刪除數(shù)據(jù)庫中的數(shù)據(jù)并關(guān)閉您的應(yīng)用程序。
黑客還可以通過執(zhí)行數(shù)據(jù)庫特定的系統(tǒng)命令來控制運(yùn)行數(shù)據(jù)庫服務(wù)器的系統(tǒng)。
到此這篇關(guān)于SpringBoot通過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)文章
Netty源碼分析NioEventLoop線程的啟動(dòng)
這篇文章主要為大家介紹了Netty源碼分析NioEventLoop線程的啟動(dòng)示例,有需要的朋友,可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03
詳解SpringBoot 解決攔截器注入Service為空問題
這篇文章主要介紹了詳解SpringBoot 解決攔截器注入Service為空問題的解決,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-06-06
java使用RandomAccessFile類基于指針讀寫文件實(shí)例代碼
這篇文章主要介紹了java使用RandomAccessFile類基于指針讀寫文件實(shí)例代碼,具有一定參考價(jià)值,需要的朋友可以了解下。2017-10-10
使用Java實(shí)現(xiàn)百萬Excel數(shù)據(jù)導(dǎo)出
這篇文章主要為大家詳細(xì)介紹了如何使用Java實(shí)現(xiàn)百萬Excel數(shù)據(jù)導(dǎo)出,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,有需要的小伙伴可以參考一下2024-03-03
Spring設(shè)計(jì)模式中代理模式詳細(xì)講解
如何實(shí)現(xiàn)在不修改源碼的基礎(chǔ)上實(shí)現(xiàn)代碼功能的增強(qiáng)呢?spring為我們提供了代理模式。所謂的代理模式通俗來說就是一個(gè)中介,它給某一個(gè)對(duì)象提供一個(gè)代理對(duì)象,并由代理對(duì)象控制原對(duì)象的引用,從而實(shí)現(xiàn)在不修改源碼的基礎(chǔ)上實(shí)現(xiàn)代碼功能的增強(qiáng)2023-01-01
idea中安裝VisualVM監(jiān)控jvm的圖文教程
這篇文章主要介紹了idea中安裝VisualVM監(jiān)控jvm的教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09

