|
@@ -122,6 +122,22 @@ public class SecurityUtils {
|
|
|
Pattern.compile("0x[0-9a-f]+", Pattern.CASE_INSENSITIVE)
|
|
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 攻击
|
|
* 检测 XSS 攻击
|
|
|
*
|
|
*
|
|
@@ -158,6 +174,13 @@ public class SecurityUtils {
|
|
|
return false;
|
|
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 decodedValue = urlDecode(value);
|
|
|
String lowerValue = normalizeSqlValue(decodedValue);
|
|
String lowerValue = normalizeSqlValue(decodedValue);
|
|
|
|
|
|
|
@@ -187,6 +210,28 @@ public class SecurityUtils {
|
|
|
return false;
|
|
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 攻击内容(转义特殊字符)
|
|
* 清理 XSS 攻击内容(转义特殊字符)
|
|
|
*
|
|
*
|
|
@@ -244,6 +289,26 @@ public class SecurityUtils {
|
|
|
return normalized.trim();
|
|
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) {
|
|
private static String safeLogContent(String value) {
|
|
|
String content = value.replaceAll("[\\r\\n\\t]+", " ");
|
|
String content = value.replaceAll("[\\r\\n\\t]+", " ");
|
|
|
if (content.length() > 200) {
|
|
if (content.length() > 200) {
|
|
@@ -259,7 +324,7 @@ public class SecurityUtils {
|
|
|
* @return 如果输入安全返回 true,否则返回 false
|
|
* @return 如果输入安全返回 true,否则返回 false
|
|
|
*/
|
|
*/
|
|
|
public static boolean isSafeInput(String value) {
|
|
public static boolean isSafeInput(String value) {
|
|
|
- return !containsXss(value) && !containsSqlInjection(value);
|
|
|
|
|
|
|
+ return !containsXss(value) && !containsSqlInjection(value) && !containsJndiInjection(value);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|