【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.mzph.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…