java matcher遍历_关于regex:Java:使用CPU利用率高的Matcher.find

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

取消长期运行的正则表达式比赛?。

清理用户请求大约需要500毫秒,而CPU%会激增。 我在测试套件运行程序中使用visualvm.java.net进行了分析。

CPU配置文件输出

377a7aac1521751fc37f9a4686d00556.png

请帮助我减少CPU使用率和平均负载吗?

根据屏幕快照,checkPattern被调用212148825次,总计6100774ms,即每次调用0.02ms。我在那里看不到性能问题-绝对没有每次调用500毫秒的证明。

如果存在导致较长时间延迟的特殊模式,则应识别它们并将其包括在问题中。

@Holger花费的时间不是问题。我只关心负载和cpu使用情况。我想对参数进行并行处理,如果这样做,则平均负载为>4。我使用jstack -l进行了线程转储,并使用thread -H -b -p找到了最大消耗线程,并将id转换为十六进制代码,该线程消耗了高CPU(50%)在Matcher.find处于可运行状态。

@bharathi:还是一样的问题。许多模式很可能很便宜,但其中有些很昂贵。您必须确定昂贵的工具才能集中精力优化这些特定模式。顺便说一句,Matcher.find会调用其他由于分析器的默认过滤规则而未显示的方法。更改这些规则以允许查看JDK的方法可以在大多数时间(意味着CPU负载)花费在匹配的内部位置上。

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

For each request , I go through all parameters and check against all these 500 regular expressions

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

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

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

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

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

还要注意,否定验证通常很容易被绕开。有人只是在其恶意代码中更改了几个字符,而您的正则表达式将不匹配。您必须扩展正则表达式的"数据库",以防止受到新的攻击。肯定验证(列入白名单)没有这个缺点,并且更有效。

嗨,我做了前两个步骤。现在,总执行时间比以前要好。获得约60ms。

我建议你看这篇论文:

"为了更快的字符串匹配进行入侵检测或超过Snort的速度"

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

本文解释说,这种方法被Dan Gusfield描述为"精确匹配的Boyer-Moore方法"。

Boyer-Moore是一种著名的字符串匹配算法。本文介绍了博伊尔-摩尔(Boyer-Moore)用于集合匹配的一种变体。

如果可能,只编译一次正则表达式并保留它们,而不要反复(隐式)编译(尤其是在循环内)。

参见java.util.regex-Pattern.compile()的重要性?有关更多信息。

我已经在这样做了。预编译所有匹配的模式并存储其模式列表。

避免出现以下情况:

多行

不区分大小写

等等

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

这是完全错误的。为什么不对多行/大小写不敏感? Java中的正则表达式匹配基于NFA +回溯,因此区分大小写之类的东西不会对性能产生太大影响。更重要的是避免回溯,例如。*,然后进行更改(a | b | c)。

如您所说,区分大小写和多行搜索不会对性能产生太大影响。这是声明它们影响性能的另一种方法。根据性能要求,它可能是相关的,也可能不是。如果需要性能,则不得使用回溯。决不。

在这500个中,必须有一个有问题的正则表达式子集。这样的正则表达式

String s ="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB";

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

将需要数年才能完成。

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

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

如果您有大量的正则表达式,则可以使用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)/

这样,匹配器只需要运行一次即可,而不是运行四次,并且由于上面的正则表达式中编写了常见的起始部分,因此避免了很多回溯。

嗨,戴维德,我不能分组。因为,我需要获取匹配的规则(mod安全规则,每个规则都有其自己的属性)详细信息。

原则上,如果匹配的规则只是500条规则中的一部分,则可以使用上述过程为每个程序包制作一个较大的正则表达式,以准备正则表达式程序包。当其中一个大正则表达式找到匹配项时,您可以检查构成程序包的原始规则。为使此方法有效,您应该将更可能同时出现的规则聚类。我希望它可行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值