Java:Matcher.find使用高CPU利用率

时间:2013-08-23 17:26:59

标签: java regex performance

我正在使用mod安全规则https://github.com/SpiderLabs/owasp-modsecurity-crs来清理用户输入数据。我正面临cpu射击和延迟匹配用户输入与mod安全规则正则表达式。总的来说,它包含500多个正则表达式来检查不同类型的攻击(xss,badrobots,generic和sql)。对于每个请求,我会查看所有参数并检查所有这500个正则表达式。我正在使用Matcher.find来检查参数。在这种情况下,一些参数属于无限循环,我使用以下技术解决了这个问题。

Cancelling a long running regex match?

清理用户请求大约需要500毫秒,并且cpu%会上升。我使用visualvm.java.net和我的测试套件运行程序进行了分析。

Cpu配置文件输出

enter image description here

请帮我减少cpu使用率和平均负载?

6 个答案:

答案 0 :(得分:3)

如果可能,编译你的正则表达式并保留它们,而不是反复(隐式)编译(特别是在循环中)。
有关详细信息,请参阅java.util.regex - importance of Pattern.compile()?

答案 1 :(得分:3)

我建议你看看这篇论文:   "Towards Faster String Matching for Intrusion Detection or Exceeding the Speed of Snort"

有更好的方法来进行您描述的匹配。基本上,您将要匹配的500个模式并将其编译为单个后缀树,该树可以非常有效地将输入与所有规则进行匹配。

该论文解释说,这种方法被Dan Gusfield描述为“完全集合匹配的Boyer-Moore方法”。

Boyer-Moore是众所周知的字符串匹配算法。本文描述了Boyer-Moore用于集匹配的变体。

答案 2 :(得分:3)

我认为这是问题的根源,而不是正则表达式本身:

  

对于每个请求,我会查看所有参数并检查所有这500个正则表达式

无论你的正则表达有多快,这仍然是很多工作。我不知道你有多少参数,但即使只有少数参数,每个请求仍然会检查数千个正则表达式。这会杀死你的CPU。

除了通过预编译和/或简化它们来提高正则表达式性能这些显而易见的事情之外,您可以执行以下操作来减少正则表达式检查的金额

  1. 根据参数类型使用用户输入的肯定验证。例如。如果某个参数必须是一个简单的数字,请不要浪费时间检查它是否包含恶意XML脚本。只需检查它是否匹配[0-9] +(或类似的简单)。如果是,那就没关系 - 跳过检查所有500个正则表达式。

  2. 尝试找到可以消除整个攻击类别的简单正则表达式 - 在正则表达式中找到常见的东西。如果是你有100个正则表达式检查是否存在某些HTML标记,检查内容是否首先包含至少一个HTML标记。如果没有,则立即保存以检查100个正则表达式。

  3. 缓存结果。 webapps中生成的许多参数都会重复出现。不要一遍又一遍地检查相同的内容,但只记得最终的验证结果。注意限制缓存的最大大小以避免DOS攻击。

  4. 另请注意,否定验证通常很容易绕过。有人只是更改了恶意代码中的几个字符,而且你的正则表达式不匹配。你必须增加你的regexp“数据库”,以防止新的攻击。正面验证(白名单)没有这个缺点,而且更有效。

答案 3 :(得分:2)

避免表达:

  • 不区分大小写

也许您可以考虑将正则表达式分组并根据用户输入应用一组给定的正则表达式。

答案 4 :(得分:1)

如果你有这么多的正则表达式,你可以使用trie算法(http://en.wikipedia.org/wiki/Trie)对它们进行分组(至少部分)。
我的想法是,如果您有/abc[0-9-]//abde//another example//.something else//.I run out of ideas/这样的正则表达式,则可以将它们合并为单个正则表达式< / p>

 /a(?:b(?:c[0-9-]|de)|nother example)|.(?:I run out of ideas|something else)/

通过这种方式,匹配器只能运行一次而不是四次,并且你可以避免大量的回溯,因为常见的起始部分是如何在上面的正则表达式中编写的。

答案 5 :(得分:1)

在这500个中必须有一部分有问题的正则表达式.I.e。这样的正则表达式

    String s = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB";

    Pattern.compile("(A+)+").matcher(s).matches();

需要数年才能完成。

因此,在您的情况下,我会记录所有有问题的正则表达式及其有问题的输入。找到这些后,您可以手动重写这些有问题的正则表达式并测试它们与原始版本。可以使用更简单,更易读的java函数重写正则表达式。

另一种选择,虽然它无法解决上述问题,但您也可以使用更快(在某些情况下为x20)和更有限的正则表达式library。它可以在Maven Central中找到。