首页/开发/正则与编码速查/灾难性回溯的成因与识别
正则进阶45 条

灾难性回溯的成因与识别

灾难性回溯由重叠或嵌套的不定长重复加上整体匹配失败造成,耗时随输入长度呈指数增长,属于可被恶意构造的拒绝服务风险。缓解手段是重构掉重叠选择点、限制输入长度、设置匹配超时,必要时改用无回溯引擎。

正则与编码速查开发更新于 2026-10-17
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌​​‌​‌‌‌‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌​​‌‌‌​‌‌​​‌​‌​‌‌‌‌​​​​​‌​‌‌‌‌​‌‌​​​‌‌​‌‌​​​​‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌‌​​‌‌​‌‌‌​‌​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​‌​​​​‌‌​‌​​‌​‌‌​​​‌‌​​‌​‌‌​‌​‌‌​​​‌​​‌‌​​​​‌​‌‌​​​‌‌​‌‌​‌​‌‌​‌‌‌​‌​​​‌‌‌​​‌​​‌‌​​​​‌​‌‌​​​‌‌​‌‌​‌​‌‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌‌‌​​‌​‌‌​‌​‌‌​​​‌‌​‌‌​​​​‌​‌‌‌​‌​‌​‌‌‌​​‌‌​‌‌​​‌​‌​​‌​‌‌‌​​‌‌​‌​​​​‌‌‌​‌​​​‌‌​‌‌​‌​‌‌​‌‌​​

灾难性回溯的成因

当模式里存在多个能匹配重叠内容的不定长重复,而整体又很可能失败时,引擎要穷举的分配组合数会随输入长度呈指数增长。这就是灾难性回溯:少量额外字符就能让耗时从毫秒跳到分钟。

为什么会失败得那么慢

失败是关键条件。如果输入很容易匹配成功,引擎很早就返回;一旦输入让整条模式在最后一步失败,所有中间尝试都必须走完,代价才会全部显现。因此攻击者只需构造一个「差一点就能匹配」的长输入。

基本信息

触发条件重叠重复、嵌套重复或高度重复的分支,加上整体匹配失败
耗时特征输入长度每增加一个字符,耗时可能翻倍
典型表现进程占用单个核心持续满载、接口超时、服务不可用
安全含义属于拒绝服务类风险,输入来源不可控时尤其危险
识别手段用逐步加长的输入测耗时,观察是否呈指数曲线
缓解方向重构模式、限制输入长度、加匹配超时、改用无回溯引擎

典型使用场景

  • 排查接口偶发超时且集中在特定输入上
  • 对用户输入做正则校验前做安全检查,避免被构造输入拖垮
  • 代码评审时识别人工写出的危险模式
  • 为关键路径加匹配超时与长度上限作为兜底
  • 把危险模式重构为等价的线性行为模式

危险模式与安全改法

危险模式与安全改法
危险: (a+)+$            重叠的嵌套重复,长串 a 加非 a 结尾即可拖满 CPU
安全: a+$               去掉外层重复,语义等价且无重叠选择点

危险: (a|a)*b           分支重叠,每个位置都有多条等价路径
安全: a*b               合并等价分支

危险: ([a-z]+)*@         嵌套重复叠加在字符类上
安全: [a-z]+@            同上,去掉多余外层

危险: .*.*=           两个点号重复相邻,选择点相乘
安全: [^=]*=            用精确字符类收窄范围

兜底: 输入长度上限 + 匹配超时 + 无回溯引擎(如不需要反向引用)

危险特征速查

特征风险改造方向
括号内加号再加外层加号极高去掉外层重复
相邻的两个点号重复高改为精确字符类
分支内容高度重叠高合并或调整分支顺序
未加锚点的长模式中到高补锚点缩小尝试起点
重复结构内部含可选中到高明确必填与可选边界
涉及用户输入的正则都应按可被恶意构造来设计:加输入长度上限与匹配超时是必要的兜底手段,仅靠优化模式不足以覆盖所有攻击形态。
验证是否修复到位,必须用加长输入实测耗时曲线,而不是只看小样本通过。若耗时随长度线性增长,说明危险结构已被消除。

易错点

  • 用固定短样本测试性能,上线后遇到长输入才出现长时间卡顿
  • 只加超时不改模式,超时频繁触发导致功能不可用
  • 把模式改得看起来更复杂但保留了重叠选择点,风险依旧存在
  • 在共享线程池里执行不可控输入的正则匹配,一次卡顿拖垮整批请求

数据来源与参考

  • OWASP 正则表达式拒绝服务(ReDoS)防护指南
  • PCRE2 官方文档 pcre2pattern(3):回溯与性能警告
  • Russ Cox 关于正则匹配性能与回溯代价的分析

整理校对:其其网ICT文档中心 · 本页地址:docs.qiqicto.com/regex/catastrophic-backtracking-cause.html

​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌​​‌​‌‌‌‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌​​‌‌‌​‌‌​​‌​‌​‌‌‌‌​​​​​‌​‌‌‌‌​‌‌​​​‌‌​‌‌​​​​‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌‌​​‌‌​‌‌‌​‌​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​‌​​​​‌‌​‌​​‌​‌‌​​​‌‌​​‌​‌‌​‌​‌‌​​​‌​​‌‌​​​​‌​‌‌​​​‌‌​‌‌​‌​‌‌​‌‌‌​‌​​​‌‌‌​​‌​​‌‌​​​​‌​‌‌​​​‌‌​‌‌​‌​‌‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌‌‌​​‌​‌‌​‌​‌‌​​​‌‌​‌‌​​​​‌​‌‌‌​‌​‌​‌‌‌​​‌‌​‌‌​​‌​‌​​‌​‌‌‌​​‌‌​‌​​​​‌‌‌​‌​​​‌‌​‌‌​‌​‌‌​‌‌​​