
正则表达式若存在嵌套量词、回溯失控等缺陷,可能被恶意输入触发指数级回溯,导致cpu长时间占用——这种redos漏洞在java等语言中真实存在,需通过模式审查、超时控制与引擎选型三重策略主动防御。
正则表达式若存在嵌套量词、回溯失控等缺陷,可能被恶意输入触发指数级回溯,导致cpu长时间占用——这种redos漏洞在java等语言中真实存在,需通过模式审查、超时控制与引擎选型三重策略主动防御。
正则表达式(Regex)是文本处理的利器,但其底层回溯机制也埋藏着严重安全隐患:恶意构造的“邪恶正则”(Evil Regex)可引发指数级时间复杂度,造成服务拒绝(ReDoS)。如题中示例——".*.*.*...b.*.*...x"含60个嵌套.*,仅输入"aaabccc"即可使Java String.matches()耗时1.5秒;当.*增至50对时,运行时间将突破分钟级。这并非性能瓶颈,而是传统NFA引擎固有的回溯爆炸(Catastrophic Backtracking)问题。
? 为什么ReDoS会发生?
Java默认使用传统NFA(Non-Deterministic Finite Automaton)正则引擎,支持强大功能(如懒惰匹配.*?、捕获组、反向引用),但代价是:当模式含重复嵌套量词(如(a+)+、(.*)*、.*X.*Y)且输入不匹配终态时,引擎会尝试所有可能的回溯路径。路径数随输入长度呈指数增长(O(2ⁿ)甚至O(eⁿ)),形成“回溯风暴”。
// 危险示例:典型ReDoS模式
String evilRegex = "(a+)+b"; // 嵌套量词
String safeRegex = "a+b"; // 线性匹配,安全
// 测试对比(谨慎执行!)
System.out.println("aaax".matches(evilRegex)); // 可能卡顿数秒
System.out.println("aaax".matches(safeRegex)); // 瞬间返回 false✅ 三大防御策略
1. 静态模式审查(Pre-Validation)
在接收用户提交的正则前,自动检测高危结构:
- 连续重复的贪婪量词:.*.*、.+.+、[a-z]*[a-z]*
- 嵌套捕获组+量词:(a+)+、([abc]+)+
- 重叠可选分支:(a|aa)+
✅ 推荐工具:
- regexdos-checker(开源Python库,可集成至Java服务)
- 自定义规则扫描器(使用ANTLR解析正则AST)
2. 运行时超时控制(Timeout Protection)
Java 9+ 提供 Pattern.compile(regex, Pattern.CANON_EQ) 但仍无原生超时。必须自行封装:
import java.util.concurrent.*;
import java.util.regex.Pattern;
public class SafeRegexMatcher {
private static final long TIMEOUT_MS = 100; // 严格限制100ms
public static boolean matches(String input, String regex) throws TimeoutException {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Boolean> future = executor.submit(() ->
Pattern.compile(regex).matcher(input).find()
);
try {
return future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
future.cancel(true);
throw new TimeoutException("Regex evaluation timed out: " + regex);
} catch (Exception e) {
future.cancel(true);
throw new RuntimeException("Regex execution failed", e);
} finally {
executor.shutdown();
}
}
}
// 使用示例
try {
boolean result = SafeRegexMatcher.matches("aaabccc", ".*b.*x");
} catch (TimeoutException e) {
// 拦截危险正则,记录告警并拒绝请求
log.warn("Rejected potentially malicious regex: {}", regex);
}3. 选用更安全的引擎(Engine Replacement)
若业务允许,替换为DFA或POSIX兼容引擎(牺牲部分语法特性换取确定性):
- RE2J:Google RE2的Java端口,无回溯、线性时间O(n),支持大部分常用语法(不支持反向引用、懒惰匹配)。
- dk.brics.automaton:基于有限自动机,适合高并发文本过滤场景。
<!-- Maven依赖 -->
<dependency>
<groupId>com.google.re2j</groupId>
<artifactId>re2j</artifactId>
<version>1.4</version>
</dependency>import com.google.re2j.Pattern;
import com.google.re2j.Matcher;
// RE2J确保O(n)性能
Pattern pattern = Pattern.compile(".*b.*x"); // 编译阶段即验证合法性
Matcher matcher = pattern.matcher("aaabccc");
boolean result = matcher.find(); // 恒定毫秒级响应⚠️ 关键注意事项
- 永远不要信任用户输入的正则:API层必须强制校验+超时+降级(如拒绝含.*.*、(?=.*a)(?=.*b)等模式)。
- 避免在循环中动态编译正则:Pattern.compile()开销大,应缓存Pattern实例(static final)。
- 警惕“看似安全”的模式:^([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})$ 若未限制邮箱长度,仍可能被"a@b." + "c".repeat(1000)触发回溯。
- 监控与告警:对Pattern.compile()和Matcher.find()调用打点,统计P99耗时,异常升高立即熔断。
正则不是黑盒——理解其引擎原理、建立防御纵深(静态扫描+运行时超时+安全引擎),才能将ReDoS从“未知风险”转化为“可控边界”。在开放正则能力的API设计中,防御性编程不是过度设计,而是生产环境的生存底线。

















