Un atac de tip Regular Expression Denial of Service (ReDoS) este una dintre cele mai periculoase vulnerabilități din aplicațiile web moderne. Spre deosebire de un atac DDoS tradițional (care necesită rețele botnet de mii de calculatoare pentru a suprasolicita lățimea de bandă a serverului), un atac ReDoS necesită un singur request HTTP malițios cu un payload de doar 40-50 de octeți pentru a bloca procesorul serverului la 100% timp de câteva minute sau ore, provocând indisponibilitatea totală a serviciului.
Anatomia unei expresii vulnerabile la ReDoS (Evil Regex)
// Exemplu clasic de expresie vulnerabilă:
^ (a+)+ $
// 1. Dacă textul este "aaaa": se potrivește în câteva fracțiuni de milisecundă.
// 2. Dacă textul este "aaaaaaaaaaaaaaaaaaaaaaaaaaaa!" (28 caractere terminate cu semnal invalid):
// Motorul NFA încearcă să împartă cele 28 de 'a'-uri între paranteza interioară și cea exterioară.
// Numărul de combinări posibile este 2^28 = 268.435.456 de pași de backtracking!
// Procesorul rămâne blocat într-o buclă infinită consumând 100% din resursele nodului.
Cele 3 Șabloane Antipattern Periculoase (Evil Patterns)
| Tipar Vulnerabil | Exemplu | Unde Apare Frecvent |
|---|---|---|
| Cuantificatori Imbricați | (x+)+ sau ([a-zA-Z]+)* |
Validări naive de nume de utilizatori, tag-uri HTML sau descrieri de produse. |
| Alternări cu Suprapunere în Cuantificator | (a|a)+$ sau (\w+|\d+)+ |
Când ambele ramuri ale operatorului | pot consuma aceleași caractere. |
| Cuantificatori Adiacenți Lacomi | .*=.*=.* |
Parsare de fișiere de configurare sau query strings fără delimitatori stricți. |
Tehnici Inginerești de Neutralizare și Refactorizare
Refactorizarea sigură: Înlocuirea cu cuantificatori posesivi sau grupuri atomice
# VULNERABIL LA ReDoS:
pattern_rau = r"^([a-zA-Z0-9]+_?)+$"
# REFACTORIZAT SIGUR (Eliminăm ambiguitatea: un cuvânt urmat opțional de underscore):
pattern_bun = r"^[a-zA-Z0-9]+(?:_[a-zA-Z0-9]+)*$"
# SAU folosind grupuri atomice în motoare compatibile (PCRE / Python regex):
pattern_atomic = r"^(?>[a-zA-Z0-9]+_?)+$"
# Odată ce un bloc a fost citit, motorul NU mai revine înapoi pe el!
Mecanisme de Apărare în Producție
- Limitarea Lungimii Inputului: Înainte de a trimite un string către o expresie regulată, validați-i lungimea:
if len(input_text) > 256: return False. ReDoS depinde exponențial de numărul de caractere. - Setarea de Timeout-uri pe Execuție: În Python 3.11+ sau .NET, configurați întotdeauna un timeout maxim (de exemplu 200 ms) pentru execuția oricărui regex primit de la utilizator.
- Audit Automat în CI/CD: Folosiți instrumente automate de analiză statică precum
safe-regexsauvuln-regex-detectorpentru a prinde expresiile vulnerabile la commit.