|
|
@@ -1,226 +0,0 @@
|
|
|
-package org.jeecg.config.firewall.interceptor;
|
|
|
-
|
|
|
-import com.alibaba.fastjson.JSON;
|
|
|
-import com.alibaba.fastjson.JSONObject;
|
|
|
-import lombok.extern.slf4j.Slf4j;
|
|
|
-import org.jeecg.common.api.vo.Result;
|
|
|
-import org.jeecg.common.exception.JeecgSqlInjectionException;
|
|
|
-import org.jeecg.common.util.SqlInjectionUtil;
|
|
|
-import org.jeecg.config.sign.util.BodyReaderHttpServletRequestWrapper;
|
|
|
-import org.springframework.web.servlet.HandlerInterceptor;
|
|
|
-
|
|
|
-import javax.servlet.http.HttpServletRequest;
|
|
|
-import javax.servlet.http.HttpServletResponse;
|
|
|
-import java.io.IOException;
|
|
|
-import java.io.PrintWriter;
|
|
|
-import java.util.Enumeration;
|
|
|
-import java.util.Map;
|
|
|
-
|
|
|
-/**
|
|
|
- * SQL注入全局拦截器
|
|
|
- * 拦截所有请求参数,防止SQL注入攻击
|
|
|
- *
|
|
|
- * @author system
|
|
|
- * @date 2025-12-05
|
|
|
- */
|
|
|
-@Slf4j
|
|
|
-public class SqlInjectionInterceptor implements HandlerInterceptor {
|
|
|
-
|
|
|
- /**
|
|
|
- * 在请求处理之前进行调用
|
|
|
- */
|
|
|
- @Override
|
|
|
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
|
|
- try {
|
|
|
- String requestURI = request.getRequestURI();
|
|
|
- log.debug("SQL注入拦截器检查请求: {}", requestURI);
|
|
|
-
|
|
|
- // 检查URL参数
|
|
|
- checkUrlParameters(request);
|
|
|
-
|
|
|
- // 检查请求头参数(排除常见的安全请求头)
|
|
|
- checkHeaders(request);
|
|
|
-
|
|
|
- // 检查POST请求的JSON Body
|
|
|
- checkRequestBody(request);
|
|
|
-
|
|
|
- return true;
|
|
|
- } catch (JeecgSqlInjectionException e) {
|
|
|
- log.error("检测到SQL注入攻击! URI: {}, 错误信息: {}", request.getRequestURI(), e.getMessage());
|
|
|
- handleSqlInjectionException(response, e.getMessage());
|
|
|
- return false;
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("SQL注入拦截器异常: ", e);
|
|
|
- return true; // 其他异常不阻止请求继续
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 检查URL参数
|
|
|
- */
|
|
|
- private void checkUrlParameters(HttpServletRequest request) {
|
|
|
- Enumeration<String> parameterNames = request.getParameterNames();
|
|
|
- while (parameterNames.hasMoreElements()) {
|
|
|
- String paramName = parameterNames.nextElement();
|
|
|
- String[] paramValues = request.getParameterValues(paramName);
|
|
|
-
|
|
|
- if (paramValues != null) {
|
|
|
- for (String paramValue : paramValues) {
|
|
|
- if (paramValue != null && !paramValue.trim().isEmpty()) {
|
|
|
- // 进行SQL注入检测
|
|
|
- SqlInjectionUtil.filterContent(paramValue, null);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 检查请求头(仅检查自定义请求头,排除标准HTTP请求头)
|
|
|
- */
|
|
|
- private void checkHeaders(HttpServletRequest request) {
|
|
|
- Enumeration<String> headerNames = request.getHeaderNames();
|
|
|
- while (headerNames.hasMoreElements()) {
|
|
|
- String headerName = headerNames.nextElement();
|
|
|
-
|
|
|
- // 排除标准HTTP请求头和常见的安全请求头
|
|
|
- if (isStandardHeader(headerName)) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- String headerValue = request.getHeader(headerName);
|
|
|
- if (headerValue != null && !headerValue.trim().isEmpty()) {
|
|
|
- // 进行SQL注入检测
|
|
|
- SqlInjectionUtil.filterContent(headerValue, null);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 检查POST请求的JSON Body
|
|
|
- */
|
|
|
- private void checkRequestBody(HttpServletRequest request) {
|
|
|
- // 只检查POST请求
|
|
|
- if (!"POST".equalsIgnoreCase(request.getMethod())) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // 如果是BodyReaderHttpServletRequestWrapper,说明已经被包装过,可以读取body
|
|
|
- if (request instanceof BodyReaderHttpServletRequestWrapper) {
|
|
|
- BodyReaderHttpServletRequestWrapper wrapper = (BodyReaderHttpServletRequestWrapper) request;
|
|
|
- String bodyString = wrapper.getBodyString(request);
|
|
|
-
|
|
|
- log.info("[SQL注入检查] 开始检查POST请求Body, URI: {}, Body: {}", request.getRequestURI(), bodyString);
|
|
|
-
|
|
|
- if (bodyString != null && !bodyString.trim().isEmpty()) {
|
|
|
- try {
|
|
|
- // 尝试解析为JSON
|
|
|
- JSONObject jsonObject = JSON.parseObject(bodyString);
|
|
|
- if (jsonObject != null) {
|
|
|
- // 递归检查JSON中的所有字符串值
|
|
|
- checkJsonValues(jsonObject);
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- // 如果不是JSON格式,直接检查整个body字符串
|
|
|
- log.debug("请求body不是JSON格式,直接检查内容");
|
|
|
- SqlInjectionUtil.filterContent(bodyString, null);
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- log.warn("[SQL注入检查] POST请求未被BodyReaderHttpServletRequestWrapper包装! URI: {}, Request类型: {}",
|
|
|
- request.getRequestURI(), request.getClass().getName());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 递归检查JSON中的所有字符串值
|
|
|
- */
|
|
|
- private void checkJsonValues(Object obj) {
|
|
|
- if (obj == null) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (obj instanceof String) {
|
|
|
- String value = (String) obj;
|
|
|
- if (!value.trim().isEmpty()) {
|
|
|
- log.debug("[SQL注入检查] 检查字符串值: {}", value);
|
|
|
- SqlInjectionUtil.filterContent(value, null);
|
|
|
- }
|
|
|
- } else if (obj instanceof JSONObject) {
|
|
|
- JSONObject jsonObject = (JSONObject) obj;
|
|
|
- for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
|
|
|
- log.debug("[SQL注入检查] 检查JSON字段: {} = {}", entry.getKey(), entry.getValue());
|
|
|
- checkJsonValues(entry.getValue());
|
|
|
- }
|
|
|
- } else if (obj instanceof Map) {
|
|
|
- Map<?, ?> map = (Map<?, ?>) obj;
|
|
|
- for (Object value : map.values()) {
|
|
|
- checkJsonValues(value);
|
|
|
- }
|
|
|
- } else if (obj instanceof Iterable) {
|
|
|
- for (Object item : (Iterable<?>) obj) {
|
|
|
- checkJsonValues(item);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 判断是否为标准HTTP请求头
|
|
|
- */
|
|
|
- private boolean isStandardHeader(String headerName) {
|
|
|
- if (headerName == null) {
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- String lowerHeaderName = headerName.toLowerCase();
|
|
|
-
|
|
|
- // 标准HTTP请求头
|
|
|
- return lowerHeaderName.equals("accept")
|
|
|
- || lowerHeaderName.equals("accept-encoding")
|
|
|
- || lowerHeaderName.equals("accept-language")
|
|
|
- || lowerHeaderName.equals("authorization")
|
|
|
- || lowerHeaderName.equals("cache-control")
|
|
|
- || lowerHeaderName.equals("connection")
|
|
|
- || lowerHeaderName.equals("content-type")
|
|
|
- || lowerHeaderName.equals("content-length")
|
|
|
- || lowerHeaderName.equals("cookie")
|
|
|
- || lowerHeaderName.equals("host")
|
|
|
- || lowerHeaderName.equals("origin")
|
|
|
- || lowerHeaderName.equals("referer")
|
|
|
- || lowerHeaderName.equals("user-agent")
|
|
|
- || lowerHeaderName.equals("x-requested-with")
|
|
|
- || lowerHeaderName.equals("x-forwarded-for")
|
|
|
- || lowerHeaderName.equals("x-forwarded-proto")
|
|
|
- || lowerHeaderName.equals("x-real-ip")
|
|
|
- // Shiro和JWT相关
|
|
|
- || lowerHeaderName.equals("x-access-token")
|
|
|
- || lowerHeaderName.equals("x-sign")
|
|
|
- || lowerHeaderName.equals("x-timestamp")
|
|
|
- // CORS相关
|
|
|
- || lowerHeaderName.startsWith("access-control-")
|
|
|
- || lowerHeaderName.startsWith("sec-");
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 处理SQL注入异常,返回错误信息
|
|
|
- */
|
|
|
- private void handleSqlInjectionException(HttpServletResponse response, String message) {
|
|
|
- response.setCharacterEncoding("UTF-8");
|
|
|
- response.setContentType("application/json; charset=utf-8");
|
|
|
- response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
|
|
-
|
|
|
- PrintWriter writer = null;
|
|
|
- try {
|
|
|
- Result<Object> result = Result.error(400, "请求参数存在安全风险,禁止访问!");
|
|
|
- writer = response.getWriter();
|
|
|
- writer.write(JSON.toJSONString(result));
|
|
|
- writer.flush();
|
|
|
- } catch (IOException e) {
|
|
|
- log.error("输出SQL注入错误信息失败", e);
|
|
|
- } finally {
|
|
|
- if (writer != null) {
|
|
|
- writer.close();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-}
|