SpringBoot通过Filter实现整个项目接口的SQL注入拦截详解
作者:w7h1te
这篇文章主要介绍了SpringBoot通过Filter实现整个项目接口的SQL注入拦截详解,SQL注入是比较常见的网络攻击方式之一,在客户端在向服务器发送请求的时候,sql命令通过表单提交或者url字符串拼接传递到后台持久层,最终达到欺骗服务器执行恶意的SQL命令,需要的朋友可以参考下
前言
业务背景:一般情况下,我们的项目是需要做SQL注入、XSS拦截等安全性问题,本来是打算在网关层面做全局过滤器或者自定义过滤器,但是苦于遇到依赖问题等实在短期内无法解决,于是便在其下一层针对某个项目做SQL注入拦截。
一、直接上源码
1.启动类路径下,直接建一个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); // 正则校验是否有SQL关键字 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请求获取参数 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请求 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请求,参数在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 :入参中有非法字符: " + value); HttpServletResponse response = (HttpServletResponse) servletResponse; // Map<String, String> responseMap = new HashMap<>(); // 匹配到非法字符,立即返回 // responseMap.put("code", "999"); // responseMap.put("message","入参中有非法字符"); resultMsg.setResultMsg("输入数据中存在非法字符!"); resultMsg.setSuccess(false); resultMsg.setResultCode("999"); resultMsg.setData("入参中有非法字符————SQL注入拦截!!!"); 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() { } }
设置请求装饰类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注解来诠释次配置文件相关代码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启动类上添加注解声明过滤器
代码:
@ServletComponentScan(basePackages = “com.bc.***.filter”)
三、什么是sql注入
SQL注入是比较常见的网络攻击方式之一,在客户端在向服务器发送请求的时候,sql命令通过表单提交或者url字符串拼接传递到后台持久层,最终达到欺骗服务器执行恶意的SQL命令;
它不是利用操作系统的BUG来实现攻击,而是针对程序员编程时的疏忽,通过SQL语句,实现无帐号登录,甚至篡改数据库。
sql注入可能产生的影响
恶意用户可以未经授权访问您的应用程序并窃取数据。
他们可以更改,删除数据库中的数据并关闭您的应用程序。
黑客还可以通过执行数据库特定的系统命令来控制运行数据库服务器的系统。
到此这篇关于SpringBoot通过Filter实现整个项目接口的SQL注入拦截详解的文章就介绍到这了,更多相关SpringBoot的SQL注入拦截内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!