Regex może działać natychmiast dla typowych przykładów, a po dodaniu jednego znaku blokować proces przez sekundy. Tradycyjna engine używa backtrackingu: po niepowodzeniu późniejszego fragmentu wraca do wcześniejszej decyzji i próbuje innego podziału tekstu. Gdy wiele podziałów jest równoważnych, liczba ścieżek rośnie bardzo szybko. Na niezaufanym wejściu staje się to atakiem Regular Expression Denial of Service.
Backtracking sam w sobie jest użyteczny
Silnik wybiera drogę przez alternatywy i powtórzenia. Jeśli suffix nie pasuje, cofa się i oddaje część znaków poprzedniemu elementowi. Dzięki temu greedy quantifiers i elastyczne wzorce zachowują się intuicyjnie.
Problem zaczyna się, gdy ten sam fragment można podzielić na bardzo wiele sposobów. Ostateczny brak match wymaga wtedy sprawdzenia niemal całego drzewa decyzji.
Zagnieżdżone quantifiers są sygnałem ostrzegawczym
Wzorzec podobny do (a+)+ może rozłożyć serię liter a na wiele grup wewnętrznych. Jeśli na końcu oczekiwane b nie wystąpi, engine testuje coraz więcej kombinacji.
Nie każdy zagnieżdżony quantifier jest podatny, ale nakładające się możliwości wymagają review i pomiaru. Im mniej jednoznaczne jest przypisanie znaku do fragmentu, tym większe ryzyko.
Alternatywy ze wspólnym prefiksem tworzą podobny problem
(a|aa)+ dzieli długi ciąg a na wiele sposobów. Realne wzorce z kilkoma podobnymi słowami mogą zachowywać się tak samo. Brak końcowego suffixu uruchamia kosztowną eksplorację.
Wspólny prefiks warto wyciągnąć, a gałęzie rozróżniać możliwie wcześnie. Bardziej deterministyczna gramatyka jest zwykle czytelniejsza.
Nieograniczona kropka rozszerza przestrzeń szukania
.* może pochłonąć prawie cały input. Kilka takich elementów i opcjonalnych grup tworzy wiele możliwych granic. Klasa wykluczająca separator opisuje zakres dokładniej.
Limit długości ma wartość zarówno domenową, jak i wydajnościową. Pole o maksymalnie 200 znakach nie potrzebuje nieskończonego quantifiera.
Poprawny input często ukrywa wadę
Na pasującym tekście engine szybko znajduje pierwszą drogę. Worst case to zwykle bardzo długi poprawny prefiks zakończony jednym błędnym znakiem. Testy performance muszą zawierać takie non-matches.
Warto mierzyć rosnące długości. Jeśli dwukrotnie większy input zwiększa czas wielokrotnie, zachowanie nie jest liniowe i wymaga analizy.
Possessive i atomic ograniczają cofanie
Possessive quantifier nie oddaje pochłoniętego tekstu, a atomic group zabrania wracania do wcześniejszych decyzji wewnątrz grupy. Mogą usunąć ogromną liczbę alternatywnych ścieżek.
Zmieniają jednak język, jeżeli późniejszy fragment naprawdę potrzebuje części znaków. Należy je stosować po zrozumieniu granicy i chronić testami.
Engine liniowa usuwa całą klasę ryzyka
RE2 i podobne implementacje gwarantują liniowy czas dla wspieranej składni. Rezygnują między innymi z niektórych backreferences i lookarounds. Dla niezaufanych danych jest to często korzystny kompromis.
Migracja istniejących patternów może wymagać uproszczenia. Nowe walidacje powinny jednak odpowiedzieć, czy dodatkowa moc tradycyjnej engine jest rzeczywiście potrzebna.
Timeout jest drugą linią obrony
Niektóre platformy pozwalają ustawić limit czasu lub kroków regex. Chroni to pojedynczy proces przed nieskończoną pracą, ale wiele równoległych requestów nadal może zużyć zasoby przed timeoutem.
Limit należy łączyć z poprawą wzorca, ograniczeniem inputu i rate limiting. Przerwanie powinno być mierzone jako osobna kategoria błędu.
Limit wejścia powinien działać wcześniej
E-mail, username czy route mają sensowny maksymalny rozmiar. Serwer powinien odrzucić ogromną wartość przed regex, normalizacją Unicode i bazą. HTTP oraz pole tworzą wspólny budżet kosztu.
Trzeba rozróżnić bajty, codepoints i code units. Unicode może mieć różną długość zależnie od warstwy.
Dynamiczny pattern zwiększa ryzyko
Jeżeli użytkownik może dostarczyć własny regex, potrafi celowo stworzyć kosztowną konstrukcję. Escaping pomaga tylko wtedy, gdy wejście miało być literalem. Prawdziwa funkcja wyszukiwania potrzebuje bezpiecznej engine lub ograniczonego języka zapytań.
Pattern zmieniany w panelu administracyjnym powinien przechodzić taki sam review jak kod. Konfiguracja również może zablokować produkcję.
Analiza statyczna daje sygnały, nie pełny dowód
Narzędzia wykrywają nested quantifiers i nakładające się alternatywy. Mogą zgłaszać false positives, lecz pomagają wskazać ryzykowne miejsca. Ostateczną odpowiedź daje pomiar z celowo trudnym inputem.
W produkcji warto mierzyć czas match, timeouty i długość danych bez logowania pełnej treści.
Performance budget powinien być częścią testu
Zamiast sprawdzać wyłącznie, czy funkcja zakończyła się „w miarę szybko”, można ustalić maksymalny rozmiar wejścia i oczekiwany rząd wzrostu. Benchmark dla kilku długości wykrywa regresję, nawet jeśli wszystkie pojedyncze uruchomienia mieszczą się jeszcze w timeout.
Absolutny czas zależy od CI i hardware, dlatego ważniejszy bywa stosunek między rozmiarami oraz twardy limit ochronny w produkcji. Dane testowe powinny obejmować zarówno match, jak i prawie pasujący non-match.
Regex w route i firewallu wymaga wspólnej interpretacji
Gateway może używać innej engine niż aplikacja. Ten sam wzorzec po translacji może inaczej traktować Unicode, newline lub anchory. Jeśli pierwsza warstwa przyznaje dostęp, a druga wybiera zasób, rozbieżność staje się problemem bezpieczeństwa.
Najważniejsze reguły warto testować end-to-end przez rzeczywistą infrastrukturę. Kopiowanie patternu między narzędziami bez porównania semantyki nie zapewnia spójności.
Mały parser może być prostszy i szybszy
Format z kilkoma separatorami da się rozdzielić i sprawdzić w liniowych krokach. Każda część otrzymuje osobny błąd, a koszt jest przewidywalny.
Krótszy source nie zawsze oznacza prostszy algorytm. Jawna pętla bywa bezpieczniejsza od sprytnego patternu.
ReDoS jest zwykłym problemem walidacji wejścia
Bezpieczny regex ma jednoznaczne powtórzenia, limit danych i testy długich non-matches. Liniowa engine daje dodatkową gwarancję, a timeout ogranicza skutki pomyłki.
Wzorzec powinien rozpoznawać tekst, a nie eksplorować ogromną liczbę możliwych interpretacji. Gdy dróg jest zbyt wiele, trzeba uprościć gramatykę albo zmienić narzędzie.