Regex 我怎么能认出一个邪恶的正则表达式?

Regex 我怎么能认出一个邪恶的正则表达式?,regex,Regex,我最近意识到了攻击,并决定根除所谓的“邪恶”正则表达式模式,只要我能在代码库中找到它们——或者至少是那些用于用户输入的模式。上面和中给出的示例很有帮助,但它们不能很好地用简单的术语解释问题 邪恶正则表达式的描述,来自: 正则表达式将重复(“+”,“*”)应用于复杂的子表达式 对于重复的子表达式,存在一个匹配项,该匹配项也是另一个有效匹配项的后缀 举例来说,同样来自: (a+)+ ([a-zA-Z]+)* (a | aa)+ (a | a?+ (.*a){x}对于x>10 这是一个没有更简

我最近意识到了攻击,并决定根除所谓的“邪恶”正则表达式模式,只要我能在代码库中找到它们——或者至少是那些用于用户输入的模式。上面和中给出的示例很有帮助,但它们不能很好地用简单的术语解释问题

邪恶正则表达式的描述,来自:

  • 正则表达式将重复(“+”,“*”)应用于复杂的子表达式
  • 对于重复的子表达式,存在一个匹配项,该匹配项也是另一个有效匹配项的后缀
举例来说,同样来自:

  • (a+)+
  • ([a-zA-Z]+)*
  • (a | aa)+
  • (a | a?+
  • (.*a){x}
    对于x>10

这是一个没有更简单解释的问题吗?我正在寻找一种方法,在编写正则表达式时可以更容易地避免这个问题,或者在现有的代码库中找到它们。

我认为您无法识别这样的正则表达式,至少不能识别所有正则表达式,也不能不限制它们的表达能力。如果您真的关心重做,我会尝试对它们进行沙箱处理,并通过超时终止它们的处理。也有可能存在允许您限制其最大回溯量的正则表达式实现。

我将其总结为“重复的重复”。您列出的第一个示例很好,因为它指出“字母a在一行中出现一次或多次。这可能在一行中再次出现一次或多次”

在这种情况下,要寻找的是量词的组合,比如*和+

第三个和第四个是需要注意的更微妙的事情。这些示例包含一个OR操作,其中双方都可以为真。这与表达式的量词相结合,会根据输入字符串产生大量潜在的匹配

总而言之,TLDR风格:

注意量词如何与其他运算符结合使用。

为什么邪恶的正则表达式会成为问题? 因为计算机完全按照你告诉他们的去做,即使这不是你的意思或者完全不合理。如果您要求正则表达式引擎证明,对于某些给定输入,给定模式存在或不存在匹配,那么无论必须测试多少种不同的组合,该引擎都会尝试这样做

以下是一个简单的模式,灵感来自OP文章中的第一个示例:

^((ab)*)+$
根据输入:

阿巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴

正则表达式引擎尝试类似于
(abababababab)
的东西,并且在第一次尝试时找到匹配项

但是我们把活动扳手扔进去:

阿巴巴巴a

发动机将首先尝试
(abababababababab)
,但由于额外的
a
而失败。这会导致灾难性的括号跟踪,因为我们的模式
(ab)*
,出于诚意,将释放其中一个捕获(它将“回溯”)并让外部模式重试。对于我们的正则表达式引擎,它看起来像这样:

(ababababab)
-不
(abababab)(ab)
-不
(abababab)(abab)
-不
(abababab)(ab)(ab)
-不
(abababab)(ababab)
-不
(abababab)(abab)(ab)
-不
(abababab)(ab)(abab)
-不
(abababab)(ab)(ab)(ab)
-不
(abababab)(abababab)
-不
(abababab)(ababab)(ab)
-不
(ababab)(abab)(abab)
-不
(ababab)(abab)(ab)(ab)
-不
(abababab)(ab)(ababab)
-不
(ababab)(ab)(abab)(ab)
-不
(ababab)(ab)(ab)(abab)
-不
(ababab)(ab)(ab)(ab)(ab)(ab)
-不
(ababababab)(ababababab)
-不
(ababababab)(abababab)(ab)
-不
(abababab)(ababab)(abab)
-不
(abababab)(ababab)(ab)(ab)(ab)
-不
(abababab)(abab)(abab)(ab)
-不
(abababab)(abab)(ab)(abab)(abab)
-不
(abababab)(abab)(ab)(ab)(ab)(ab)
-不
(abababab)(ab)(abababab)
-不
(abababab)(ab)(ababab)(ab)
-不
(ababab)(ab)(abab)(abab)(abab)
-不
(ababab)(ab)(abab)(ab)(ab)(ab)
-不
(abababab)(ab)(ab)(ababab)
-不
(ababababab)(ab)(ab)(abab)(abab)(ab)
-不
(abababab)(ab)(ab)(ab)(abab)(abab)
-不
(abababab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)
-不
..
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(abababab)
-不
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(abababab)(ab)
-Nope
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(abab)(abab)(abab)(abab)(abab)
-不
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(abab)(abab)(ab)(ab)(ab)(ab)(ab)(ab)
-Nope
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(abababab)(abababab)
-Nope
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(abab)(abab)(ab)
-Nope
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(abab)(abab)(abab)
-Nope
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)
-不

可能的组合数量随着输入的长度呈指数级增长,在您知道之前,正则表达式引擎正在消耗您所有的系统资源,试图解决这个问题,直到用尽所有可能的术语组合后,它最终放弃并报告“没有匹配项”。与此同时,您的服务器已经打开
string pattern = @"^<([a-z]+)([^<]+)*(?:>(.*)<\/\1>|\s+\/>)$";
Regex regexTags = new Regex(pattern, RegexOptions.None, TimeSpan.FromSeconds(1.0));
try
{
    string noTags = regexTags.Replace(description, "");
    System.Console.WriteLine(noTags);
} 
catch (RegexMatchTimeoutException ex)
{
    System.Console.WriteLine("RegEx match timeout");
}