Aikido

Perché dovresti fare attenzione alle espressioni regolari lente per prevenire gli attacchi ReDoS

Leggibilità

Regola
Proteggiti dalle espressioni regolari lente.
Le espressioni regolari con quantificatori annidati 
o pattern ambigui possono causare 
un backtracking catastrofico e problemi di prestazioni.
Linguaggi supportati: 45+

Introduzione

Le espressioni regolari possono bloccare la tua applicazione per secondi o minuti se ricevono l'input giusto. Il backtracking catastrofico si verifica quando i motori regex esplorano percorsi in crescita esponenziale mentre cercano di far corrispondere un pattern. Una regex come (a+)+b impiega microsecondi per trovare la corrispondenza con un input valido, ma può richiedere ore per rifiutare una stringa di 'a' senza una 'b' finale. Gli attaccanti sfruttano questa vulnerabilità tramite attacchi di tipo Regular Expression Denial of Service (ReDoS), inviando input appositamente predisposti che fanno sì che il motore regex consumi il 100% della CPU fino allo scadere dei timeout delle richieste o all'arresto anomalo del processo.

Perché è importante

Implicazioni sulla sicurezza (attacchi ReDoS): un utente malintenzionato può paralizzare la tua applicazione con una singola richiesta contenente un input manipolato. I pattern di convalida delle email e di analisi degli URL sono bersagli comuni. A differenza dei tradizionali attacchi DoS che richiedono larghezza di banda, il ReDoS necessita solo di payload piccolissimi.

Degradazione delle prestazioni: il normale input dell'utente può innescare un backtracking catastrofico, facendo impennare i tempi di risposta da millisecondi a secondi. Questo crea una latenza imprevedibile e difficile da debuggare, poiché si manifesta solo con specifici pattern di input.

Incidenti in produzione: una regex vulnerabile blocca il ciclo degli eventi in Node.js o consuma le risorse del pool di thread. Con l'accumularsi delle richieste, la memoria aumenta e il sistema non risponde più. Nei microservizi, una singola regex vulnerabile propaga i guasti ai servizi dipendenti.

Difficoltà di rilevamento: i pattern che funzionano perfettamente nei test con input brevi diventano esponenzialmente più lenti con input più lunghi. La vulnerabilità spesso passa inosservata fino alla produzione, richiedendo un deployment d'emergenza durante un incidente attivo.

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_\\-\\.]+)+ creano un backtracking esponenziale. Per un'email come aaaaaaaaaaaaaaaaaaaaaaaaa!, il motore regex tenta innumerevoli combinazioni prima di fallire. La regex dell'URL presenta molteplici quantificatori annidati che aggravano il problema, rendendolo facilmente sfruttabile con input costituiti da lunghe stringhe di caratteri validi privi della struttura attesa.

✅ 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 nidificati elimina il backtracking catastrofico. I quantificatori singoli come [a-zA-Z0-9_\-\.]+ vengono eseguiti in tempo lineare. Il pattern URL utilizza gruppi di non-cattura con un suffisso opzionale (?:...)? anziché ripetizioni nidificate, garantendo prestazioni prevedibili indipendentemente dalla lunghezza o dal contenuto dell'input.

Conclusione

Le prestazioni delle espressioni regolari rappresentano un problema di sicurezza, non solo di ottimizzazione. Rivedi tutti i pattern regex alla ricerca di quantificatori nidificati, classi di caratteri sovrapposte nei gruppi di ripetizione e alternative ambigue. Testa i pattern regex con input patologici (stringhe lunghe di caratteri validi seguiti da terminazioni non valide) per identificare il backtracking catastrofico prima del deployment. Quando possibile, sostituisci le regex complesse con funzioni di parsing delle stringhe che presentano caratteristiche di prestazioni prevedibili.

FAQ

Hai domande?

Quali pattern causano il backtracking catastrofico?

Tra i colpevoli più comuni figurano quantificatori nidificati come (a+)+, (a*)* o (a+)*b, alternanze con pattern sovrapposti come (a|a)* o (a|ab)*, e ripetizioni con componenti opzionali come (a?)+. Qualsiasi pattern in cui il motore regex può trovare corrispondenze per la stessa sottostringa in modi multipli crea uno spazio di ricerca esponenziale. Fai attenzione ai quantificatori (+, *, {n,m}) all'interno di gruppi a loro volta quantificati.

Come posso testare se la mia regex è vulnerabile a ReDoS?

Utilizza strumenti online come regex101.com, che mostrano i passaggi di esecuzione e avvisano in caso di backtracking catastrofico. 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!" (più di 30 'a', senza 'b'). Se l'esecuzione richiede più di qualche millisecondo, la regex è vulnerabile. Implementa timeout nelle operazioni regex in produzione come misura di difesa in profondità.

Qual è la differenza tra backtracking catastrofico e lineare?

Il backtracking lineare si verifica quando la regex prova le alternative in sequenza senza tuttavia riconsiderare le scelte precedenti. Il carico di lavoro cresce linearmente con le dimensioni dell'input. Il backtracking catastrofico si manifesta invece quando i quantificatori nidificati costringono il motore a tentare 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 dimensioni di input moderate, è tra millisecondi e minuti.

Posso usare lookahead e lookbehind in sicurezza?

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 regex che prevengono il backtracking catastrofico?

RE2 (utilizzato da Google) garantisce un'esecuzione in tempo lineare vietando del tutto il backtracking. Non supporta tutte le funzionalità (riferimenti retrospettivi, lookaround), ma previene completamente le vulnerabilità ReDoS. Per controlli di sicurezza critici, valuta l'uso di binding di RE2 o motori simili. Per JavaScript non esiste un'alternativa integrata, pertanto la progettazione dei pattern e l'uso dei timeout costituiscono le tue difese principali.

Dovrei aggiungere timeout a tutte le operazioni con le regex?

Per gli input non attendibili (dati forniti dagli utenti, risposte di API esterne), la risposta è sì. Imposta timeout ragionevoli, ad esempio 100-500 ms a seconda della complessità prevista. In Node.js non è possibile applicare un timeout direttamente a regex.test(), ma puoi validare preventivamente la lunghezza dell'input o eseguire la regex in un worker thread con un timeout. Rifiuta gli input che superano limiti di lunghezza ragionevoli prima di tentare il matching con la regex.

Come posso correggere un pattern regex vulnerabile esistente?

Innanzitutto, verifica se hai realmente bisogno di una regex. Molte attività di validazione risultano più semplici utilizzando metodi per le stringhe come includes(), startsWith() o split(). Se l'uso di una regex è necessario, elimina i quantificatori nidificati appiattendo il pattern. Sostituisci (a+)+ con a+. Se il tuo motore regex li supporta, utilizza gruppi atomici o quantificatori possessivi. Per pattern complessi, valuta la possibilità di analizzare l'input in più passaggi avvalendoti di regex o operazioni sulle stringhe più semplici.

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.