White paper · 6 novembre 2026

Il rate limiting che blocca il servizio: anatomia di un denial of service a basso carico

Punti chiave

  • Un'API mostrava una latenza al 95° percentile di circa 20 secondi a bassa concorrenza e di circa 50 millisecondi a concorrenza più alta. Gli errori non aumentavano.
  • La causa era una query di rate limiting eseguita in un middleware globale, a ogni richiesta, prima dell'autorizzazione.
  • Con pochi worker impegnati, ogni richiesta aspettava la stessa query. Ottimizzare solo il web server non ha eliminato il problema.
  • La stessa impostazione consentiva un denial of service a basso volume: è un problema di sicurezza, oltre che di prestazioni.
  • La correzione raccomandata sposta il conteggio su un archivio in memoria e respinge le richieste non valide prima di toccare il database.

1. Il sintomo

Durante un test di carico su un'API in produzione, i risultati sembravano capovolti. Tra 10 e circa 160 utenti concorrenti, il tempo di risposta al 95° percentile era di circa 20 secondi. Oltre i 170-290 utenti scendeva di colpo a circa 50 millisecondi.

Altri segnali andavano nella stessa direzione. Le richieste autenticate e anonime avevano curve quasi identiche. Dominava il tempo al primo byte. Gli errori non aumentavano e il throughput restava stabile.

Latenza p95 riportata in funzione degli utenti concorrenti: circa 20 secondi fino a 160 utenti, poi un calo improvviso a circa 50 millisecondi

Figura 1. Latenza in funzione degli utenti concorrenti, ridisegnata dagli intervalli riportati. Non sono misure grezze.

Un sistema che diventa più veloce quando lo usano più persone è insolito. Di solito significa che le richieste aspettano qualcosa di condiviso, e che l'attesa si diluisce quando ne arrivano di più.

2. Trovare la causa

Il primo passo è stato l'ottimizzazione dell'infrastruttura: numero di worker PHP, connessioni concorrenti, timeout e buffering sul web server. Ha aiutato. La soglia a cui il servizio restava stabile è passata da circa 60 utenti concorrenti a valori molto più alti. Non ha eliminato l'alta latenza a basso carico, il che ha mostrato che la causa era nell'applicazione.

Lo slow log del gestore dei processi PHP ha poi mostrato decine di worker bloccati nello stesso momento, nello stesso punto: una chiamata al database fatta da un middleware globale. Quel middleware girava prima dell'autorizzazione e di qualsiasi controller, quindi a ogni richiesta, comprese quelle destinate a finire con una risposta 401.

Percorso della richiesta: il middleware globale esegue una query sincrona al database a ogni richiesta, prima dell'autorizzazione

Figura 2. Dove si trovava la query del rate limit nel percorso della richiesta.

La query realizzava il rate limiting. Contava le righe di una tabella di log degli ultimi 60 secondi, con una condizione OR su client e indirizzo IP, e univa altre due tabelle per ricavare le impostazioni del client.

3. Perché era peggio a basso carico

La spiegazione emersa dall'analisi è la serializzazione. Con pochi worker impegnati, ogni richiesta arrivava al middleware e aspettava la stessa query, così l'intero pool risultava di fatto bloccato su di essa. Con più richieste concorrenti la contesa si distribuiva su più connessioni e la latenza scendeva.

A sinistra, pochi worker impegnati che aspettano la stessa query. A destra, più richieste concorrenti con la contesa distribuita su più connessioni

Figura 3. Un'immagine semplificata del meccanismo.

Per questo un test di carico breve ad alta concorrenza può sembrare sano, mentre gli utenti reali con traffico moderato soffrono.

4. Perché è un problema di sicurezza

Un controllo pensato per limitare gli abusi era diventato il modo più economico per provocarli.

  • Costo prima dell'autenticazione. Un chiamante anonimo faceva lavorare il database a ogni richiesta.
  • Denial of service a basso volume. Poche richieste potevano occupare il pool di worker e degradare il servizio per tutti gli altri.
  • Scalabilità ingannevole. Il sistema sembrava sano nei test ad alta concorrenza, quindi la debolezza passava inosservata.

In un penetration test sarebbe segnalato come un problema di denial of service a livello applicativo, con le sue evidenze: il profilo di carico, lo slow log e la traccia del percorso della richiesta.

5. Cosa è stato raccomandato

Le raccomandazioni erano ordinate per impegno e rischio.

Una richiesta passa da un rifiuto anticipato e da un rate limiter su Redis prima di arrivare al controller, con il database fuori dal percorso

Figura 4. Il percorso della richiesta raccomandato.

Opzione Cosa fa Note
Rate limiting su Redis Conta le richieste con un incremento atomico e una scadenza di 60 secondi Lavoro costante per richiesta. Raccomandata
Ottimizzazione del database, temporanea Separa l'OR in conteggi distinti e aggiunge indici su client e IP con la data Un sollievo a breve termine, non una soluzione
Cache delle impostazioni del client Tiene in cache da 60 a 300 secondi i dati delle tabelle collegate Elimina le ricerche ripetute
Rifiuto anticipato Respinge le richieste palesemente non valide senza alcun accesso al database Chiude il percorso prima dell'autenticazione

Tre fasi: indici immediati e log delle query lente, mitigazione con Redis e cache, consolidamento rimuovendo il rate limit su database e verificando con un test di carico

Figura 5. Il piano raccomandato.

Oltre alla correzione immediata, le linee guida di sviluppo erano: nessuna query bloccante nel percorso globale della richiesta, middleware di sicurezza separato dalla logica di business, sistemi dedicati per il rate limiting e verifica delle prestazioni come parte del ciclo di sviluppo.

6. Verificare la tua API

  1. Esegui un test di carico a bassa concorrenza oltre che ad alta, e guarda la forma della curva.
  2. Elenca tutto ciò che la tua API fa prima di sapere chi la sta chiamando. Ogni voce è lavoro che un chiamante anonimo può provocare.
  3. Attiva lo slow log del tuo server applicativo e cerca molti worker fermi sulla stessa riga.
  4. Conta le query al database per richiesta, comprese quelle che finiscono in errore.
  5. Assicurati che il rate limiter costi lo stesso che il chiamante sia legittimo o no.

7. Limiti di questo documento

Questo documento descrive un solo intervento. I dettagli che potrebbero identificare il cliente sono stati rimossi. I numeri vengono dai test di carico e dall'analisi di quel sistema. Le correzioni indicate erano raccomandazioni: non riportiamo risultati misurati dopo la loro applicazione, e il miglioramento atteso indicato dall'analisi è una previsione, non un risultato.

Vuoi sapere cosa potrebbe far fare al tuo sistema un attaccante prima ancora che sappia chi è? Chiedici un penetration test.

Tutti i white paper