5

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

取消长时间运行的正则表达式匹配?.

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

Cpu 配置文件输出

在此处输入图像描述

请帮我降低 CPU 使用率和平均负载?

4

6 回答 6

3

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

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

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

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

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

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

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

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

于 2013-09-23T11:31:11.793 回答
3

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

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

该论文解释说,这种方法被 Dan Gusfield 描述为“Boyer-Moore Approach to Exact Set Matching”。

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

于 2013-09-19T17:59:38.453 回答
3

如果可能,编译一次正则表达式并保留它们,而不是重复(隐式)编译(尤其是在循环内)。
请参阅 java.util.regex - Pattern.compile() 的重要性?了解更多信息。

于 2013-09-18T23:06:58.130 回答
2

避免表达:

  • 多行
  • 不区分大小写
  • 等等

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

于 2013-08-31T21:14:47.240 回答
1

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

    String s = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB";

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

将需要数年时间才能完成。

因此,在您的情况下,我将记录所有有问题的正则表达式及其有问题的输入。一旦找到这些,您就可以手动重写这几个有问题的正则表达式并与原始表达式进行测试。正则表达式总是可以用更简单、更易读的 java 函数重写。

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

于 2013-09-20T14:37:24.027 回答
1

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

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

通过这种方式,匹配器只需要运行一次而不是四次,并且您避免了很多回溯,因为常见的起始部分是如何写在上面的正则表达式中的。

于 2013-09-19T14:05:54.163 回答