为什么邪恶的正则表达式有问题?
因为计算机完全按照您的要求去做,即使这不是您的意思或完全不合理。如果您要求Regex引擎证明,对于某些给定的输入,给定模式是否匹配,那么无论必须测试多少种不同的组合,引擎都将尝试这样做。
这是一个简单的模式,灵感来自OP的第一个示例:
^((ab)*)+$
给定输入:
爸爸妈妈
正则表达式引擎尝试类似的操作,(abababababababababababab)
并且在第一次尝试中找到匹配项。
但是随后我们将活动扳手投入:
ababababababababababababbab a
引擎将首先尝试,(abababababababababababab)
但是由于额外的原因而失败a
。这会导致灾难性的跟踪,因为我们的模式出于(ab)*
善意的表现,将释放其中一个捕获(它将“回溯”)并让外部模式重试。对于我们的正则表达式引擎,看起来像这样:
(abababababababababababab)
-Nope
(ababababababababababab)(ab)
-Nope
(abababababababababab)(abab)
-Nope
(abababababababababab)(ab)(ab)
-Nope
(ababababababababab)(ababab)
-Nope
(ababababababababab)(abab)(ab)
-Nope
(ababababababababab)(ab)(abab)
-Nope
(ababababababababab)(ab)(ab)(ab)
-Nope
(abababababababab)(abababab)
-Nope
(abababababababab)(ababab)(ab)
-Nope
(abababababababab)(abab)(abab)
-Nope
(abababababababab)(abab)(ab)(ab)
-Nope
(abababababababab)(ab)(ababab)
-Nope
(abababababababab)(ab)(abab)(ab)
-Nope
(abababababababab)(ab)(ab)(abab)
-Nope
(abababababababab)(ab)(ab)(ab)(ab)
-Nope
(ababababababab)(ababababab)
-Nope
(ababababababab)(abababab)(ab)
-Nope
(ababababababab)(ababab)(abab)
-Nope
(ababababababab)(ababab)(ab)(ab)
-Nope
(ababababababab)(abab)(abab)(ab)
-Nope
(ababababababab)(abab)(ab)(abab)
-Nope
(ababababababab)(abab)(ab)(ab)(ab)
-Nope
(ababababababab)(ab)(abababab)
-Nope
(ababababababab)(ab)(ababab)(ab)
-Nope
(ababababababab)(ab)(abab)(abab)
-不可以
(ababababababab)(ab)(abab)(ab)(ab)
-不可以
(ababababababab)(ab)(ab)(ababab)
-不可以
(ababababababab)(ab)(ab)(abab)(ab)
-不可以
(ababababababab)(ab)(ab)(ab)(abab)
-不可以
(ababababababab)(ab)(ab)(ab)(ab)(ab)
-不可以
...-
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(abababab)
不可以
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ababab)(ab)
-不可以
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(abab)(abab)
-不可以
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(abab)(ab)(ab)
-不可以
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ababab)
-不可以
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(abab)(ab)
-不可以
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(abab)
-不可以
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)
-不可以
可能组合的数量与输入的长度成指数关系,在不知不觉中,正则表达式引擎吞噬了所有系统资源,试图解决此问题,直到用尽所有可能的术语组合,最终放弃并报告“没有匹配项。” 同时,您的服务器已变成一堆燃烧的熔融金属。
如何发现邪恶的正则表达式
这实际上非常棘手。我已经写了一对夫妇,尽管我知道它们是什么,以及通常如何避免它们。看到Regex花了很长时间。将所有内容包装在原子组中有助于防止回溯问题。它基本上告诉正则表达式引擎不要重新访问给定的表达式-“锁定第一次尝试匹配的内容”。但是请注意,原子表达式不会阻止表达式内的回溯,因此^(?>((ab)*)+)$
仍然很危险,但是^(?>(ab)*)+$
很安全(先匹配(abababababababababababab)
然后拒绝放弃任何匹配的字符,从而防止灾难性的回溯)。
不幸的是,一旦编写完成,实际上很难立即或快速找到有问题的正则表达式。最后,识别错误的正则表达式就像识别其他任何错误的代码一样,这需要大量的时间和经验,并且/或者只发生一次灾难性事件。
有趣的是,自从这个答案首次被写出来以来,德克萨斯大学奥斯汀分校的一个小组就发表了一篇论文,描述了一种能够执行正则表达式静态分析并明确发现这些“邪恶”模式的工具的开发。该工具是为分析Java程序而开发的,但是我怀疑在未来几年中,我们将看到更多围绕JavaScript和其他语言来分析和检测有问题的模式而开发的工具,尤其是随着ReDoS攻击率的持续攀升。
静态检测使用正则表达式的程序中的DoS漏洞
ValentinWüstholz,Oswaldo Olivo,Marijn JH Heule和Isil Dillig
德州大学奥斯汀分校