Java ReDos是否容易受到攻击?

2022-09-03 02:56:58

我试图使用正则表达式重新创建正则表达式拒绝服务攻击,并使用jshell(具有大量)输入:(a+)+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!a

Pattern.compile("(a+)+")
    .matcher("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!")
    .matches()

但是每次我尝试时,这都会很快完成。Java 中的正则表达式实现是否与其他实现不同?还是链接的维基百科页面是错误的?

(顺便说一句。我正在使用Java 11,如果这相关的话)

编辑:看起来它与Java版本有关,当我在Java 8上尝试它时,它挂起了,但在Java 9和11中,它立即工作。这些版本之间有什么变化可能会影响这一点?现在在Java中所有正则表达式都是安全的吗?

是否有特定的 Java JEP 更改了正则表达式实现?我想知道什么样的正则表达式对于较新的Java来说仍然是一个问题。


答案 1

根据文章 RSPEC-2631,ReDoS 问题已在 Java 9 及更高版本中处理:

像OpenJDK 9+这样的Java运行时通过在正则表达式评估的实现中提供额外的保护来缓解这个问题。在这些运行时中,上面的示例不容易受到攻击。


答案 2

我当前正在运行Java 8,以下代码挂起:

Pattern.compile("(a|aa)+")
       .matcher("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab")
       .matches()

鉴于您如何使用Java 11(并且还使用Java 9/ 10对其进行了测试),并且已经看到它需要少量时间才能完成,显然在这些版本之间进行了更改。

通过查看 Java 11 中的源代码,我们发现以下添加内容在 Java 8 中不存在:Matcher

/**
 * Storage used by top greedy Loop node to store a specific hash set to
 * keep the beginning index of the failed repetition match. The nodes
 * themselves are stateless, so they rely on this field to hold state
 * during a match.
 */
IntHashSet[] localsPos;

这种本地存储,以及添加的大量其他代码,似乎是Java 9 +中正则表达式的状态机完成速度比Java 8及以下更快的主要原因之一。