Aikido

Perché è importante evitare l'uso di espressioni regolari lente per prevenire gli attacchi ReDoS

Leggibilità

Regola
Guardia contro il rallentamento espressioni espressioni.
Espressioni espressioni con quantificatori quantificatori o 
modelli modelli possono causare un backtracking 
passi indietro e problemi .
Lingue supportate: 45+

Introduzione

Le espressioni regolari possono bloccare l'applicazione per alcuni secondi o minuti se viene immesso l'input giusto. Il backtracking catastrofico si verifica quando i motori delle espressioni regolari esplorano percorsi che aumentano in modo esponenziale mentre cercano di trovare una corrispondenza con un pattern. Un'espressione regolare come (a+)+b Ci vogliono pochi microsecondi per verificare la validità di un input, ma possono essere necessarie ore per rifiutare una stringa composta esclusivamente da "a" senza una "b" finale. Gli hacker sfruttano questa vulnerabilità tramite attacchi di tipo "Regular Expression Denial of Service" (ReDoS), inviando input appositamente manipolati che inducono il motore delle espressioni regolari a consumare il 100% della CPU fino a quando non si verifica il timeout della richiesta o il processo va in crash.

Perché è importante

Implicazioni per la sicurezza (attacchi ReDoS): un malintenzionato può paralizzare l'applicazione con una singola richiesta contenente dati appositamente manipolati. La convalida delle e-mail e gli schemi di analisi degli URL sono bersagli comuni. A differenza degli attacchi DoS tradizionali, che richiedono larghezza di banda, gli attacchi ReDoS necessitano solo di payload minimi.

Deterioramento delle prestazioni: un normale input da parte dell'utente può innescare un backtracking catastrofico, causando un'impennata dei tempi di risposta da millisecondi a secondi. Ciò genera una latenza imprevedibile, difficile da risolvere poiché si manifesta solo con specifici modelli di input.

Incidenti di produzione: un'espressione regolare vulnerabile blocca il ciclo degli eventi in Node.js o consuma le risorse del pool di thread. Man mano che le richieste si accumulano, l'utilizzo della memoria aumenta e il sistema smette di rispondere. Nei microservizi, un'espressione regolare vulnerabile provoca un effetto a cascata di errori sui servizi dipendenti.

Difficoltà di individuazione: gli schemi che funzionano correttamente durante i test con input brevi diventano esponenzialmente lenti con input più lunghi. La vulnerabilità spesso passa inosservata fino alla fase di produzione, rendendo necessaria un'implementazione d'emergenza nel corso di un incidente in corso.

Esempi di codice

❌ Non conforme:

function validateEmail(email) {
    const regex = /^([a-zA-Z0-9_\-\.]+)+@([a-zA-Z0-9_\-\.]+)+\.([a-zA-Z]{2,5})$/;
    return regex.test(email);
}

function extractURLs(text) {
    const regex = /(https?:\/\/)?([\w\-])+\.(\w+)+([\w\-\.,@?^=%&:/~\+#]*)+/g;
    return text.match(regex);
}

Perché non è sicuro: I quantificatori annidati ([a-zA-Z0-9_\\-\\.]+)+ creare un backtracking esponenziale. Per un'e-mail come aaaaaaaaaaaaaaaaaaaaaaaaa!, il motore delle espressioni regolari prova innumerevoli combinazioni prima di fallire. L'espressione regolare dell'URL presenta diversi quantificatori annidati che aggravano il problema, rendendola facilmente vulnerabile a input quali lunghe stringhe di caratteri validi prive della struttura prevista.

✅ Conforme:

function validateEmail(email) {
    const regex = /^[a-zA-Z0-9_\-\.]+@[a-zA-Z0-9_\-\.]+\.[a-zA-Z]{2,5}$/;
    return regex.test(email);
}

function extractURLs(text) {
    const regex = /https?:\/\/[\w\-]+\.[\w\-]+(?:[\w\-\.,@?^=%&:/~\+#]*)?/g;
    return text.match(regex);
}

Perché è sicuro: la rimozione dei quantificatori annidati elimina il backtracking catastrofico. I quantificatori singoli come [a-zA-Z0-9_\-\.]+ vengono eseguiti in tempo lineare. Il modello URL utilizza gruppi non catturanti con suffisso opzionale (?:...)? anziché ripetizioni annidate, garantendo prestazioni prevedibili indipendentemente dalla lunghezza o dal contenuto dell'input.

Conclusione

Le prestazioni delle espressioni regolari rappresentano un problema di sicurezza, non solo una questione di ottimizzazione. Verificate tutti i pattern di espressioni regolari alla ricerca di quantificatori annidati, classi di caratteri sovrapposte nei gruppi di ripetizione e alternative ambigue. Testate i pattern di espressioni regolari con input patologici (lunghe stringhe di caratteri validi seguite da terminazioni non valide) per individuare eventuali backtracking catastrofici prima della distribuzione. Ove possibile, sostituite le espressioni regolari complesse con funzioni di analisi delle stringhe che presentino caratteristiche prestazionali prevedibili.

Domande frequenti

Hai delle domande?

Quali schemi causano un backtracking catastrofico?

Tra i fattori più comuni figurano i quantificatori annidati come (a+)+, (a*)* o (a+)*b. L’alternanza con pattern sovrapposti come (a|a)* o (a|ab)*. La ripetizione con componenti opzionali come (a?)+. Qualsiasi pattern in cui il motore delle espressioni regolari possa trovare corrispondenze per la stessa sottostringa in più modi crea uno spazio di ricerca esponenziale. Prestate attenzione ai quantificatori (+, *, {n,m}) all’interno di gruppi che sono a loro volta quantificati.

Come posso verificare se la mia espressione regolare è vulnerabile a un attacco ReDoS?

Utilizza strumenti online come regex101.com, che mostrano le fasi di esecuzione e segnalano eventuali backtracking catastrofici. Crea input di test con lunghe stringhe di caratteri validi seguite da caratteri che forzano il backtracking. Per il pattern /^(a+)+b$/, esegui il test con "aaaaaaaaaaaaaaa!" (oltre 30 "a", nessuna "b"). Se l'esecuzione richiede più di qualche millisecondo, l'espressione regolare è vulnerabile. Implementa dei timeout nelle operazioni con espressioni regolari in produzione come misura di difesa approfondita.

Qual è la differenza tra backtracking catastrofico e lineare?

Il backtracking lineare si verifica quando l'espressione regolare prova le alternative in sequenza senza rivalutare le scelte precedenti. Il carico di lavoro cresce linearmente con la dimensione dell'input. Il backtracking catastrofico si verifica quando i quantificatori annidati costringono il motore a provare un numero esponenziale di combinazioni. Per un input di lunghezza n, il tempo di esecuzione può essere O(2^n) o peggiore. La differenza, per input di dimensioni modeste, va da pochi millisecondi a diversi minuti.

Posso utilizzare i lookahead e i lookbehind in modo sicuro?

Lookaheads (?=...) and lookbehinds (?<=...) themselves don't cause catastrophic backtracking, but they can hide vulnerable patterns. A lookahead containing (a+)+ is still vulnerable. Use lookarounds for their intended purpose (assertions without consuming characters), not as a workaround for complex matching. Keep the patterns inside lookarounds simple and test them thoroughly.

Esistono motori di espressioni regolari che impediscono il backtracking catastrofico?

RE2 (utilizzato da Google) garantisce un'esecuzione in tempo lineare vietando completamente il backtracking. Non supporta tutte le funzionalità (riferimenti all'indietro, lookaround), ma impedisce completamente il ReDoS. Per i controlli di sicurezza critici, si consiglia di utilizzare i binding RE2 o motori simili. Per JavaScript non esiste un'alternativa integrata, quindi la progettazione dei pattern e i timeout rappresentano le difese principali.

Dovrei aggiungere dei timeout a tutte le operazioni con espressioni regolari?

Per gli input non attendibili (dati forniti dall'utente, risposte da API esterne), sì. Imposta timeout ragionevoli, ad esempio tra 100 e 500 ms, a seconda della complessità prevista. In Node.js non è possibile impostare direttamente un timeout per `regex.test()`, ma è possibile verificare prima la lunghezza dell'input oppure eseguire l'espressione regolare in un thread di lavoro con un timeout. Rifiuta gli input che superano i limiti di lunghezza ragionevoli prima di tentare la corrispondenza con l'espressione regolare.

Come posso correggere un pattern regex esistente che presenta una vulnerabilità?

Innanzitutto, valuta se hai davvero bisogno delle espressioni regolari. Molte operazioni di validazione risultano più semplici utilizzando metodi per le stringhe come `includes()`, `startsWith()` o `split()`. Se le espressioni regolari sono necessarie, elimina i quantificatori annidati semplificando il pattern. Sostituisci `(a+)+` con `a+`. Utilizza gruppi atomici o quantificatori possessivi se il tuo motore di espressioni regolari li supporta. Per i pattern complessi, valuta la possibilità di analizzare l'input in più passaggi utilizzando espressioni regolari più semplici o operazioni sulle stringhe.

Metti in sicurezza ora

Metti in sicurezza il tuo codice, il cloud e il runtime in un unico sistema centralizzato.
Trova e risolvi le vulnerabilità rapidamente e automaticamente.

Nessuna carta di credito richiesta | Risultati della scansione in 32 secondi.