J'ai essayé de recréer attaque par déni de service d'expression régulière en utilisant (a+)+
regexp et aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!
(avec de grandes quantités de a
) en utilisant jshell:
Pattern.compile("(a+)+")
.matcher("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!")
.matches()
Mais cela se termine assez rapidement à chaque fois que j'ai essayé. L'implémentation de regexp en Java est-elle différente des autres? Ou la page wikipedia liée est incorrecte?
(BTW. J'utilise Java 11, si c'est pertinent)
EDIT: Il semble que cela soit lié à la version Java. Lorsque je l’ai essayée sur Java 8, cela se bloque, mais cela fonctionne immédiatement avec Java 9 et 11. Qu'est-ce qui a changé entre ces versions et qui pourrait affecter ça? Est-ce que tous les regex sont en sécurité maintenant en Java?
Existe-t-il un JEP Java spécifique qui a modifié l'implémentation de regexp? J'aimerais savoir quel type de regexps pose encore problème pour les versions plus récentes de Java.
J'utilise actuellement Java 8 et le code suivant se bloque:
Pattern.compile("(a|aa)+")
.matcher("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab")
.matches()
Étant donné que vous utilisez Java 11 (et que vous l’avez également testé avec Java 9/10) et que vous avez constaté que cela prend un peu de temps, une modification a été apportée entre ces versions.
En regardant le code source de Matcher
dans Java 11, nous trouvons l’addition suivante qui n’est pas présente dans Java 8:
/**
* 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;
Ce stockage local, ainsi que de nombreux autres codes ajoutés, semble être l’une des principales raisons pour lesquelles la machine à états des expressions régulières dans Java 9+ se termine beaucoup plus rapidement que dans Java 8 et les versions ultérieures.