Ver Fonte

feat(security): 增加JNDI/Log4Shell攻击防护检测功能

- 新增JNDI/Log4Shell攻击检测正则表达式和归一化处理方法
- 在SecurityUtils中添加containsJndiInjection方法检测JNDI注入
- 扩展isSafeInput方法,增加对JNDI注入的安全校验
- 在XssAndSqlInjectionFilter中过滤用户输入时加入JNDI检测逻辑
- 添加SQL编码绕过检测的相关正则表达式及匹配逻辑
- 增强安全检测能力,防止Log4Shell及相关安全威胁
wzq há 1 semana atrás
pai
commit
9753e04b9a

+ 66 - 1
national-motion-base-core/src/main/java/org/jeecg/common/util/SecurityUtils.java

@@ -122,6 +122,22 @@ public class SecurityUtils {
             Pattern.compile("0x[0-9a-f]+", Pattern.CASE_INSENSITIVE)
     };
 
+    /**
+     * SQL 编码绕过检测正则表达式,例如 GBK 宽字节注入中的 %bf%27。
+     */
+    private static final Pattern[] SQL_ENCODING_BYPASS_PATTERNS = {
+            Pattern.compile("%(?:8[0-9a-f]|9[0-9a-f]|a[0-9a-f]|b[0-9a-f]|c[0-9a-f]|d[0-9a-f]|e[0-9a-f]|f[0-9a-f])(?:%5c|%255c)?(?:%27|%2527)", Pattern.CASE_INSENSITIVE)
+    };
+
+    /**
+     * JNDI/Log4Shell 攻击检测正则表达式
+     */
+    private static final Pattern[] JNDI_INJECTION_PATTERNS = {
+            Pattern.compile("\\$\\{jndi:", Pattern.CASE_INSENSITIVE),
+            Pattern.compile("\\$\\{[^\\r\\n]{0,300}?jndi:", Pattern.CASE_INSENSITIVE),
+            Pattern.compile("\\bjndi:(ldap|ldaps|rmi|dns|iiop|corba|nis|nds)://", Pattern.CASE_INSENSITIVE)
+    };
+
     /**
      * 检测 XSS 攻击
      *
@@ -158,6 +174,13 @@ public class SecurityUtils {
             return false;
         }
 
+        for (Pattern pattern : SQL_ENCODING_BYPASS_PATTERNS) {
+            if (pattern.matcher(value).find()) {
+                log.warn("检测到 SQL 编码绕过攻击,匹配模式: {}, 内容: {}", pattern.pattern(), safeLogContent(value));
+                return true;
+            }
+        }
+
         String decodedValue = urlDecode(value);
         String lowerValue = normalizeSqlValue(decodedValue);
 
@@ -187,6 +210,28 @@ public class SecurityUtils {
         return false;
     }
 
+    /**
+     * 检测 JNDI/Log4Shell 攻击。
+     *
+     * @param value 待检测的字符串
+     * @return 如果检测到 JNDI/Log4Shell payload 返回 true,否则返回 false
+     */
+    public static boolean containsJndiInjection(String value) {
+        if (StrUtil.isBlank(value)) {
+            return false;
+        }
+
+        String normalizedValue = normalizeJndiValue(urlDecode(value));
+        for (Pattern pattern : JNDI_INJECTION_PATTERNS) {
+            if (pattern.matcher(normalizedValue).find()) {
+                log.warn("检测到 JNDI/Log4Shell 攻击,匹配模式: {}, 内容: {}", pattern.pattern(), safeLogContent(value));
+                return true;
+            }
+        }
+
+        return false;
+    }
+
     /**
      * 清理 XSS 攻击内容(转义特殊字符)
      *
@@ -244,6 +289,26 @@ public class SecurityUtils {
         return normalized.trim();
     }
 
+    /**
+     * JNDI/Log4Shell 检测前归一化:处理 URL 编码、大小写和常见 Log4j lookup 混淆。
+     */
+    private static String normalizeJndiValue(String value) {
+        String normalized = value.toLowerCase(Locale.ROOT);
+        for (int i = 0; i < 5; i++) {
+            String previous = normalized;
+            normalized = normalized.replaceAll("\\$\\{\\s*(lower|upper)\\s*:\\s*([^{}]+)\\s*}", "$2");
+            normalized = normalized.replaceAll("\\$\\{\\s*::\\s*-\\s*([^{}]+)\\s*}", "$1");
+            normalized = normalized.replaceAll("\\$\\{\\s*[a-z0-9_.-]+\\s*:[^{}]*:-\\s*([^{}]+)\\s*}", "$1");
+            if (previous.equals(normalized)) {
+                break;
+            }
+        }
+        normalized = normalized.replace('\u00A0', ' ');
+        normalized = normalized.replaceAll("[\\r\\n\\t\\u0000]+", "");
+        normalized = normalized.replaceAll("\\s+", "");
+        return normalized.trim();
+    }
+
     private static String safeLogContent(String value) {
         String content = value.replaceAll("[\\r\\n\\t]+", " ");
         if (content.length() > 200) {
@@ -259,7 +324,7 @@ public class SecurityUtils {
      * @return 如果输入安全返回 true,否则返回 false
      */
     public static boolean isSafeInput(String value) {
-        return !containsXss(value) && !containsSqlInjection(value);
+        return !containsXss(value) && !containsSqlInjection(value) && !containsJndiInjection(value);
     }
 
     /**

+ 5 - 0
national-motion-base-core/src/main/java/org/jeecg/config/filter/XssAndSqlInjectionFilter.java

@@ -221,6 +221,11 @@ public class XssAndSqlInjectionFilter implements Filter {
          * @param location 内容来源位置(用于日志记录)
          */
         private void checkContent(String content, String location) {
+            // JNDI/Log4Shell 检测
+            if (SecurityUtils.containsJndiInjection(content)) {
+                throw new JeecgBootException("用户输入包含非法内容,请输入合法内容!");
+            }
+
             // XSS 检测
             if (Boolean.TRUE.equals(properties.getXssEnabled()) && SecurityUtils.containsXss(content)) {
 //                throw new JeecgBootException("检测到 XSS 攻击尝试,位置: " + location);