Zum Hauptinhalt springen

Regex testen

Der Regex-Tester wendet in Ihrem Browser ein Muster auf einen Testtext an – in einem Web Worker mit einem Timeout von zwei Sekunden. Das ist keine Vorsicht um der Vorsicht willen: JavaScript bietet keine Möglichkeit, einen laufenden regulären Ausdruck zu unterbrechen. Ein Muster wie (a+)+$ hält seinen Thread bei einer langen Eingabe also unbegrenzt fest, und den Worker zu beenden ist die einzige Verteidigung, die wirklich funktioniert.

Nur lokal
//gm
Testtext
0 B1 ZeileZ. 1, Sp. 1
Treffer

Wie funktioniert Regex testen?

Die meisten Regex-Tester führen das Muster im Haupt-Thread der Seite aus. Das funktioniert, bis jemand ein Muster einfügt, das backtracked – dann reagiert der Tab nicht mehr, und die einzige Abhilfe ist, ihn zu schließen.

Katastrophales Backtracking und warum man es nicht abfangen kann

Die Regex-Engine von JavaScript arbeitet mit Backtracking: Schlägt eine Übereinstimmung fehl, probiert sie jeden alternativen Weg erneut. Für (a+)+b gegen eine Folge von a-Zeichen, die mit etwas anderem endet, sind das alle Arten, die Folge zwischen den beiden Quantoren aufzuteilen – also exponentiell. Dreißig Zeichen sind rund eine Milliarde Versuche. Es gibt keine Timeout-Option, kein Abbruchsignal, keinen Übergabepunkt und keine Exception; die Engine kehrt schlicht nicht zurück.

Das ist die Schwachstellenklasse ReDoS, und sie legt echte Systeme lahm. Der globale Ausfall von Cloudflare 2019 war ein einzelner Regex in einer einzigen WAF-Regel; der Ausfall von Stack Overflow 2016 war ein Muster, das Whitespace findet, in einem Beitrag mit einer langen Whitespace-Folge. Beide Male auf einem Server, wo die Folge ist, dass jede Anfrage blockiert wird, statt nur ein Tab.

Ein Worker lässt sich beenden – genau darauf beruht das gesamte Design

Deshalb läuft die Suche in einem Web Worker, und ein Timer beendet ihn nach zwei Sekunden. Das ist der einzige verfügbare Mechanismus: Nichts kann einen Regex mitten in der Ausführung stoppen, aber ein Thread lässt sich zerstören. Löst das Timeout aus, erhalten Sie eine klare Meldung mit der wahrscheinlichen Ursache statt eines eingefrorenen Tabs – was auch ein nützliches Signal ist, denn ein Muster, das hier in den Timeout läuft, würde einen Server aufhängen.

Statische Analyse benennt Formen, keine Urteile

Das Risikopanel sucht nach verschachtelten Quantoren, quantifizierten Alternativen mit überlappenden Zweigen und benachbarten Quantoren über denselben Zeichen. Das deckt nahezu jeden echten Vorfall ab. Was es bewusst nicht tut, ist ein Muster für sicher zu erklären: Zu entscheiden, ob ein beliebiger Regex exponentielles Worst-Case-Verhalten hat, ist ein echtes Analyseproblem, und ein Tool, das Sicherheit verspräche, gäbe ein Versprechen, das es nicht halten kann.

Treffer der Länge null brauchen einen Anstoß

Eine globale Match-Schleife über ein Muster, das auch nichts treffen kann – a*, (?:), \b – erhöht lastIndex nie und läuft endlos. Jede korrekte Implementierung erhöht es nach einem leeren Treffer von Hand, und diese hier tut es; eine Kleinigkeit, an der erstaunlich viele Tools scheitern.

Die Aufschlüsselung erfolgt pro Token

Das Muster wird in seine Token zerlegt, jedes mit einer Erklärung in Klartext – denn das übliche Problem mit dem Regex eines anderen ist nicht, dass er falsch ist, sondern dass er unlesbar ist. Folgen von Literalzeichen werden zusammengefasst, statt einzeln aufgelistet.

Muster

(\w+)@(\w+\.\w+)

Treffer

1  [email protected]     at 58
   $1 ada  $2 example.com
2  [email protected]   at 271
   $1 grace  $2 example.org

Welche Optionen und Sonderfälle unterstützt Regex testen?

Technische Referenz und Optionen
ParameterTypStandardVerhalten & Sonderfälle
g – globalFlageinFindet jeden Treffer statt nur den ersten. Ohne dieses Flag wird nur ein Ergebnis zurückgegeben, egal wie viele es gibt.
i – Groß-/Kleinschreibung ignorierenFlagausGroß- und Kleinschreibung wird ignoriert. Zusammen mit dem Flag u kommt zusätzlich Unicode-Case-Folding zum Einsatz, das mehr abdeckt als ASCII.
m – mehrzeiligFlagein^ und $ treffen an jedem Zeilenumbruch, nicht nur an den Enden des Texts. Für log-artige Eingaben fast immer genau das, was Sie wollen.
s – dotAllFlagausSorgt dafür, dass . auch einen Zeilenumbruch trifft. Ohne dieses Flag endet .* am Zeilenende – der übliche Grund, warum ein mehrzeiliger Treffer scheitert.
u – UnicodeFlagausAktiviert \u{…}-Escapes und \p{…}-Eigenschaftsklassen und bringt die Engine dazu, mit Code Points statt UTF-16-Einheiten zu arbeiten – genau das lässt . auf ein ganzes Emoji passen.
y – stickyFlagausTrifft nur an lastIndex, ohne vorwärts zu suchen. Wird für Tokenizer verwendet, bei denen eine Lücke einen Syntaxfehler bedeutet und nichts ist, was man überspringen dürfte.
TimeoutMillisekunden2000Danach wird der Worker beendet. Ob ein Muster nur langsam oder exponentiell ist, lässt sich vorab nicht unterscheiden – die verstrichene Zeit ist das einzige verfügbare Signal.
TrefferlimitAnzahl5000Darüber hinaus werden die Ergebnisse abgeschnitten, und das Abschneiden wird gemeldet. Fünfzigtausend Zeilen zu rendern hilft niemandem.
Ersetzung$1, $<name>, $&—Nummerierte Gruppen, benannte Gruppen und der gesamte Treffer. $$ ist ein literales Dollarzeichen – genau das vergessen alle.

Häufig gestellte Fragen

Warum hat mein Muster das Timeout ausgelöst?

Sehr wahrscheinlich läuft es in katastrophales Backtracking. Suchen Sie nach einem Quantor innerhalb einer quantifizierten Gruppe – (a+)+, (\d*)* – oder nach einer quantifizierten Alternative, deren Zweige denselben Text treffen können, etwa (a|ab)+. Die Engine probiert jede Art aus, die Eingabe zwischen ihnen aufzuteilen – exponentiell in der Eingabelänge. Das Risikopanel benennt die Form, wenn es eine findet.

Wie behebe ich ein Muster, das backtracked?

Beseitigen Sie die Mehrdeutigkeit, welcher Teil was trifft. Ersetzen Sie (a+)+ durch a+. Sorgen Sie dafür, dass sich die Zweige einer Alternative gegenseitig ausschließen, sodass an jeder Position nur einer treffen kann. Verankern Sie das Muster, damit ein Fehlschlag nicht von jedem Zeichen aus neu ansetzt. Begrenzen Sie offene Wiederholungen auf das, was die Daten tatsächlich enthalten. Und ziehen Sie eine konkrete Zeichenklasse einem Wildcard vor: [^"]* backtracked weit weniger als .*.

Ist ReDoS ein echtes Risiko oder ein theoretisches?

Echt, und teuer. Der globale Ausfall von Cloudflare im Juli 2019 war ein einzelner regulärer Ausdruck in einer WAF-Regel; Stack Overflow fiel 2016 wegen eines Musters aus, das abschließenden Whitespace findet, als ein Beitrag eine lange Folge davon enthielt. Auf einem Server ist das Fehlerbild schlimmer als hier: Jede Anfrage wartet hinter der einen, die in der Engine feststeckt – eine einzige konstruierte Eingabe legt den Dienst lahm.

Warum geht mein .* nicht über Zeilenumbrüche hinweg?

Weil . keinen Zeilenumbruch trifft, solange das Flag s nicht gesetzt ist. Das ist die häufigste Regex-Überraschung in JavaScript überhaupt, und man verwechselt sie leicht mit dem Flag m – aber m ändert, was ^ und $ bedeuten, nicht was . trifft. Für einen mehrzeiligen Treffer wollen Sie s, für zeilenverankertes Matching m; beides ist unabhängig, und oft braucht man beides.

Sollte ich eine E-Mail-Adresse mit einem Regex prüfen?

Nur grob. Die Grammatik für eine Adresse in RFC 5322 kommt als Muster auf mehrere hundert Zeichen, akzeptiert Dinge, mit denen niemand rechnet – Local Parts in Anführungszeichen, Kommentare, Domains als IP-Literal – und sagt Ihnen trotzdem nicht, ob das Postfach existiert. Das nützliche Muster prüft auf ein @ mit etwas davor und danach und einen Punkt in der Domain; die eigentliche Validierung ist, eine Bestätigungsmail zu schicken.

Wird mein Muster oder mein Text hochgeladen?

Nein. Die Suche läuft in einem Web Worker in Ihrem Browser – ein separater Thread im selben Tab, kein Server. Prüfen können Sie das im Netzwerk-Panel. Das ist wichtig, weil der Text, gegen den Menschen testen, meist ein Produktions-Log ist – und Produktions-Logs enthalten E-Mail-Adressen, Tokens und Kundenkennungen.