从一次CPU打满到ReDos攻击和防范
字数 1215 2025-08-11 08:35:44
ReDos攻击原理与防范措施详解
1. ReDos攻击概述
ReDos(Regular Expression Denial of Service,正则表达式拒绝服务攻击)是一种通过构造特殊字符串,使正则表达式引擎消耗大量CPU和内存资源,导致服务器资源耗尽的攻击方式。
1.1 攻击特征
- 导致CPU使用率飙升(如案例中达到99%)
- 系统响应缓慢
- 大量相同任务线程堆积
- 正则表达式匹配过程出现阻塞
2. 正则表达式引擎原理
2.1 两种实现方式
DFA自动机(确定有限状态自动机)
- 构造代价大但执行效率高
- 时间复杂度:O(n),n为字符串长度
- 不支持回溯
NFA自动机(非确定有限状态自动机)
- 构造代价小但执行效率低
- 时间复杂度:O(ns),n为字符串长度,s为状态数
- 支持回溯和高级功能
- 大多数编程语言采用此实现
2.2 NFA自动机特性
- 有限的状态集合S
- 输入符号集合Σ(不包括空字符ε)
- 状态迁移函数F
- 初始状态s0
- 结束状态集S子集
3. ReDos攻击原理
3.1 指数级回溯示例
正则表达式:^(a+)+$
匹配字符串:aaaaX
- 需要进行2^4=16次尝试才能否定匹配
- 字符串
aaaaaaaaaaX需要2^10=1024次尝试 - 随着a的数量增加,匹配尝试呈指数增长
3.2 问题根源
- 嵌套量词(如(a+)+)
- 重叠选择分支
- 复杂的回溯路径
- 恶意构造的长字符串
4. 常见ReDos场景(Java示例)
4.1 参数验证
@Pattern(regexp = "^(GB)\\d{9}", message = "VAT号必须以GB开头,9位数字结尾")
private String vatNumber;
4.2 业务数据验证
String regExp = "^[1-9]\\d{3}-(0?[1-9]|1[0-2])-(0?[1-9]|[1-2][0-9]|3[0-1])$";
if (!outstockDto.getInvoiceDate().matches(regExp)) {
// ...
}
4.3 字符串替换
String newStr = str.replaceAll(regex, replacement);
4.4 配置文件匹配
joybuy.login.domain = .*fop\.joybuy\.com$
pulsar.login.domain = .*ifop\.jd\.com$
5. ReDos检测方法
5.1 RegexStaticAnalysis工具
- 使用Maven打包
- 执行本地运行
- 输入测试的正则表达式
5.2 在线测试工具(regex101.com)
- 输入正则表达式和测试字符串
- 查看匹配步数和结果
- 使用debugger模式查看详细匹配过程
6. 防范措施
6.1 设计原则
- 降低正则表达式复杂度
- 减少分组使用
- 避免嵌套量词
- 限制回溯深度
6.2 实施方法
-
输入限制:
- 严格限制用户输入字符串长度
- 对输入进行预处理和过滤
-
测试验证:
- 编写单元测试
- 进行模糊测试(fuzzing)
- 使用边界值测试
-
监控机制:
- 实施性能监控(如UMP、PFinder)
- 设置CPU使用率告警阈值
-
静态分析:
- 使用静态代码分析工具扫描正则表达式
- 在CI/CD流程中加入正则安全检查
7. 解决方案示例
7.1 优化案例
原正则表达式匹配域名方式改为字符串处理:
public static String extractDomain(String url) {
if(StringUtils.isBlank(url)) {
return "";
}
int index = 0;
if(url.startsWith(HTTP)) {
index = HTTP.length();
} else if(url.startsWith(HTTPS)) {
index = HTTPS.length();
} else {
return "";
}
String safeUrl = url.substring(index);
index = safeUrl.indexOf('/');
if(index > 0) {
safeUrl = safeUrl.substring(0, index);
}
String[] array = safeUrl.split("\\.");
if(array.length < 2) {
return "";
}
String part1 = array[array.length - 2];
String part2 = array[array.length - 1];
if(StringUtils.isNotBlank(part1) && StringUtils.isNotBlank(part2)) {
if(!isIn(part2, DOMAINS)) {
return "";
}
return part1 + '.' + part2;
}
return "";
}
7.2 替代方案
- 使用专用URL解析库替代正则
- 实现白名单机制
- 对关键操作设置超时限制
8. 总结
ReDos攻击是一种容易被忽视但危害严重的安全威胁,防范关键在于:
- 理解正则表达式引擎原理
- 避免编写存在回溯爆炸风险的正则
- 实施多层防御措施
- 建立完善的监控和响应机制
通过合理的设计、严格的测试和持续的监控,可以有效降低ReDos攻击的风险。