从一次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自动机特性

  1. 有限的状态集合S
  2. 输入符号集合Σ(不包括空字符ε)
  3. 状态迁移函数F
  4. 初始状态s0
  5. 结束状态集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工具

  1. 使用Maven打包
  2. 执行本地运行
  3. 输入测试的正则表达式

5.2 在线测试工具(regex101.com)

  • 输入正则表达式和测试字符串
  • 查看匹配步数和结果
  • 使用debugger模式查看详细匹配过程

6. 防范措施

6.1 设计原则

  1. 降低正则表达式复杂度
  2. 减少分组使用
  3. 避免嵌套量词
  4. 限制回溯深度

6.2 实施方法

  1. 输入限制

    • 严格限制用户输入字符串长度
    • 对输入进行预处理和过滤
  2. 测试验证

    • 编写单元测试
    • 进行模糊测试(fuzzing)
    • 使用边界值测试
  3. 监控机制

    • 实施性能监控(如UMP、PFinder)
    • 设置CPU使用率告警阈值
  4. 静态分析

    • 使用静态代码分析工具扫描正则表达式
    • 在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 替代方案

  1. 使用专用URL解析库替代正则
  2. 实现白名单机制
  3. 对关键操作设置超时限制

8. 总结

ReDos攻击是一种容易被忽视但危害严重的安全威胁,防范关键在于:

  1. 理解正则表达式引擎原理
  2. 避免编写存在回溯爆炸风险的正则
  3. 实施多层防御措施
  4. 建立完善的监控和响应机制

通过合理的设计、严格的测试和持续的监控,可以有效降低ReDos攻击的风险。

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 参数验证 4.2 业务数据验证 4.3 字符串替换 4.4 配置文件匹配 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 优化案例 原正则表达式匹配域名方式改为字符串处理: 7.2 替代方案 使用专用URL解析库替代正则 实现白名单机制 对关键操作设置超时限制 8. 总结 ReDos攻击是一种容易被忽视但危害严重的安全威胁,防范关键在于: 理解正则表达式引擎原理 避免编写存在回溯爆炸风险的正则 实施多层防御措施 建立完善的监控和响应机制 通过合理的设计、严格的测试和持续的监控,可以有效降低ReDos攻击的风险。