Springboot詳解如何實(shí)現(xiàn)SQL注入過濾器過程
場景:以過濾器(Filter)的方式,對所有http請求的入?yún)r截,使用正則表達(dá)式匹配入?yún)⒅械淖址4嬖赟QL注入風(fēng)險(xiǎn)的參數(shù),中斷請求,并立即返回提示信息。不存在SQL注入風(fēng)險(xiǎn)的參數(shù),校驗(yàn)通過后,放入過濾器鏈,繼續(xù)后續(xù)業(yè)務(wù)。
環(huán)境:本例是基于springboot的web工程,版本:springboot 2.6.3
1.過濾器SqlInjectFilter
SqlInjectFilter,實(shí)現(xiàn)javax.servlet.Filter接口。即在doFilter方法中實(shí)現(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);
// 正則校驗(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請求獲取參數(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
在攔截請求時(shí),會讀取HttpServletRequest的InputStream,而這種數(shù)據(jù)流一旦讀取后,就沒了。那么直接把請求放入過濾器鏈,后續(xù)的環(huán)節(jié)就讀取不到數(shù)據(jù)了。因此,需要一個(gè)裝飾類,讀取了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詳解如何實(shí)現(xiàn)SQL注入過濾器過程的文章就介紹到這了,更多相關(guān)Springboot SQL注入過濾器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Springboot應(yīng)用中過濾器如何修改response的header和body內(nèi)容
- SpringBoot項(xiàng)目如何設(shè)置權(quán)限攔截器和過濾器
- SpringBoot整合Spring?Security過濾器鏈加載執(zhí)行流程源碼分析(最新推薦)
- springBoot 過濾器去除請求參數(shù)前后空格實(shí)例詳解
- Springboot如何設(shè)置過濾器及重復(fù)讀取request里的body
- springboot webflux 過濾器(使用RouterFunction實(shí)現(xiàn))
- Springboot實(shí)現(xiàn)過濾器的兩種方式
相關(guān)文章
IDEA連接Mysql數(shù)據(jù)庫的詳細(xì)圖文教程
項(xiàng)目開發(fā)時(shí)使用Intellij IDEA連接本地?cái)?shù)據(jù)庫,將數(shù)據(jù)庫可視化,還可對數(shù)據(jù)庫表直接進(jìn)行增刪改查操作,方便快捷又清晰,下面這篇文章主要給大家介紹了關(guān)于IDEA連接Mysql數(shù)據(jù)庫的詳細(xì)圖文教程,需要的朋友可以參考下2023-03-03
使用@RequestBody配合@Valid校驗(yàn)入?yún)?shù)
這篇文章主要介紹了使用@RequestBody配合@Valid校驗(yàn)入?yún)?shù),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
詳解java爬蟲jsoup解析多空格class數(shù)據(jù)
在本篇內(nèi)容中小編給大家分享了java爬蟲jsoup怎么解析多空格class數(shù)據(jù)的方法和技巧,需要的朋友們跟著學(xué)習(xí)下。2018-12-12
Spring Cloud Alibaba配置多環(huán)境管理詳解與實(shí)戰(zhàn)代碼
本文通過實(shí)際案例詳細(xì)介紹了springboot配置多環(huán)境管理的使用,以及基于nacos的配置多環(huán)境管理的實(shí)踐,在實(shí)際開發(fā)中,配置多環(huán)境管理是一個(gè)很難避開的問題,同時(shí)也是微服務(wù)治理中一個(gè)很重要的內(nèi)容,感興趣的朋友跟隨小編一起看看吧2024-06-06
聊聊Spring循環(huán)依賴三級緩存是否可以減少為二級緩存的情況
這篇文章主要介紹了聊聊Spring循環(huán)依賴三級緩存是否可以減少為二級緩存的情況,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02
簡單了解java函數(shù)式編碼結(jié)構(gòu)及優(yōu)勢
這篇文章主要介紹了簡單了解java函數(shù)式編碼結(jié)構(gòu)及優(yōu)勢,本文將探討三種下一代 JVM 語言:Groovy、Scala 和 Clojure,比較并對比新的功能和范例,讓 Java 開發(fā)人員對自己近期的未來發(fā)展有大體的認(rèn)識。,需要的朋友可以參考下2019-06-06
Spring Boot 3.x 全新的熱部署配置方式詳解(IntelliJ ID
這篇文章主要介紹了Spring Boot 3.x 全新的熱部署配置方式(IntelliJ IDEA 2023.1),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07
Java實(shí)現(xiàn)經(jīng)典游戲俄羅斯方塊(升級版)的示例代碼
俄羅斯方塊是一款風(fēng)靡全球,從一開始到現(xiàn)在都一直經(jīng)久不衰的電腦、手機(jī)、掌上游戲機(jī)產(chǎn)品,是一款游戲規(guī)則簡單,但又不缺乏樂趣的簡單經(jīng)典小游戲。本文將用Java語言實(shí)現(xiàn)這一經(jīng)典游戲,需要的可以參考一下2022-09-09

