Il rate limiting è fondamentale per proteggere le risorse di un’applicazione o di un sito web da un uso eccessivo o improprio. Che sia il risultato di un intervento umano malevolo, di attacchi basati su bot o di una vulnerabilità trascurata, l’uso improprio delle risorse può interferire con l’accesso legittimo alla vostra applicazione e introdurre gravi vulnerabilità.

Questo articolo spiega come aggiungere il rate limiting a un’API in un’applicazione Laravel.

Limitare il traffico delle API in Laravel

Il rate limiting è un meccanismo progettato per mitigare lo sfruttamento delle risorse di un’applicazione. Sebbene abbia molti usi, è particolarmente utile per le API pubbliche in sistemi scalabili di grandi dimensioni. Garantisce a tutti gli utenti legittimi un accesso equo alle risorse del sistema.

Il rate limiting è fondamentale anche per la sicurezza, il controllo dei costi e la stabilità generale del sistema. Può aiutare a prevenire gli attacchi basati sulle richieste, come gli attacchi DDoS (distributed denial-of-service). Questo attacco si basa sull’invio di richieste ripetute per sovraccaricare e interrompere l’accesso al server di un’applicazione o di un sito web.

Esistono diversi metodi per implementare il rate limiting. Si possono utilizzare delle variabili che caratterizzano il richiedente per determinare chi può accedere a un’applicazione e con quale frequenza. Alcune variabili comuni sono:

  • Indirizzo IP – L’implementazione di rate limiting basati sugli indirizzi IP permette di limitare il numero di richieste per indirizzo. Questo metodo è particolarmente vantaggioso nei casi in cui gli utenti possono accedere all’applicazione senza fornire le credenziali.
  • Chiave API – Limitare l’accesso tramite chiavi API significa fornire al richiedente delle chiavi API pre-generate e stabilire dei rate limiting per ogni chiave. Con questo approccio, si possono anche applicare diversi livelli di accesso alle chiavi API generate.
  • ID cliente – Si può anche pre-generare un ID cliente che l’utente può inserire nell’intestazione o nel corpo delle richieste API. Questo metodo permette di impostare livelli di accesso per ID per garantire che nessun client possa monopolizzare le risorse del sistema.

Middleware di Laravel

Il middleware fornisce un comodo meccanismo per ispezionare e filtrare le richieste HTTP che entrano in un’applicazione. In sostanza, si tratta di uno strato di codice tra l’applicazione e l’infrastruttura sottostante per consentire la comunicazione tra le risorse.

Come implementare i rate limiting

Questo tutorial utilizza una mini libreria API esistente sul framework Laravel 10 per dimostrare l’uso di Laravel Throttle. Il progetto di partenza contiene le implementazioni di base per la creazione, la lettura, l’aggiornamento e la cancellazione (CRUD) necessarie per gestire i libri in una raccolta e due percorsi extra per dimostrare alcuni concetti di rate limiting.

Prerequisiti

Il tutorial presuppone che si conoscano le basi dello sviluppo di API in Laravel. Assicuratevi di avere i seguenti elementi:

Utilizzeremo anche MyKinsta per configurare e distribuire questa API. Potete anche seguire il template di progetto fornito e vedere l’anteprima del risultato finale dal codice sorgente completo.

Configurazione dell’applicazione Laravel

  1. Per iniziare, cloniamo il template di progetto.
  2. Quindi, creiamo un file .env nella directory principale del progetto e copiamo il contenuto di .env.example al suo interno.
  3. Successivamente, completiamo la configurazione utilizzando i seguenti comandi per installare le dipendenze dell’applicazione e generare la chiave dell’applicazione.
composer install
php artisan key:generate

Se questo comando non aggiunge automaticamente la chiave dell’applicazione al file .env, eseguite php artisan key:generate --show, copiate la chiave generata e incollatela nel file .env come valore per APP_KEY.

  1. Una volta completata l’installazione delle dipendenze e la generazione della chiave dell’applicazione, avviamo l’applicazione con il seguente comando:
php artisan serve

Questo comando avvia l’applicazione e la rende accessibile tramite il browser all’indirizzo https://127.0.0.1:8000.

  1. Visitiamo l’URL per verificare che la pagina di benvenuto di Laravel venga visualizzata:

La pagina di benvenuto di Laravel mostra il suo logo in alto al centro.
La schermata di benvenuto di Laravel

Configurazioni del database

Configuriamo e impostiamo il database dell’applicazione in MyKinsta.

  1. Accediamo alla dashboard dell’account MyKinsta e clicchiamo sul pulsante Aggiungi servizio:

Il segmento superiore della scheda Cruscotto di MyKinsta è caratterizzato da una barra degli strumenti superiore.
La dashboard di MyKinsta con diversi servizi configurati.

  1. Nell’elenco Aggiungi servizio, clicchiamo su Database e configuriamo i parametri per avviare l’istanza del database:

Il modulo "Crea un database" di Kinsta visualizza la prima sezione, chiamata "Dettagli di base."
Configurazione del database MyKinsta.

Questo tutorial utilizza MariaDB, ma si può scegliere una qualsiasi delle opzioni di database supportate da Laravel che Kinsta mette a disposizione.

  1. Una volta inseriti i dati del database, clicchiamo sul pulsante Continua per completare la procedura.

I database forniti da Kinsta hanno parametri di connessione interni ed esterni. Bisogna utilizzare i parametri di connessione interni per le applicazioni ospitate all’interno dello stesso account Kinsta e i parametri esterni per le connessioni esterne. Pertanto, utilizziamo le credenziali del database esterno di Kinsta per la nostra applicazione.

  1. Copiamo e aggiorniamo le credenziali .env del database dell’applicazione con le credenziali esterne mostrate nella schermata seguente:

La dashboard di Kinsta visualizza la sezione "Dettagli di base" del database "library-records" appena creato. Tutte le informazioni corrispondono a quelle inserite nella precedente immagine di configurazione del database.
Dettagli della configurazione del database di MyKinsta.

DB_CONNECTION=mysql
DB_HOST=your_host_name
DB_PORT=your_port
DB_DATABASE=your_database_info
DB_USERNAME=your_username
DB_PASSWORD=your_password
  1. Dopo aver inserito le credenziali del database, verifichiamo la connessione applicando la migrazione del database con il comando seguente:
php artisan migrate

Se tutto funziona correttamente, dovreste vedere una risposta simile a quella mostrata qui sotto.

L'output del terminale mostra il comando Bash
Migrazione del database riuscita su un terminale.

  1. Successivamente, usiamo il seguente comando per elencare i percorsi dell’applicazione e vedere i percorsi già implementati.
php artisan route:list

A questo punto, dovremmo vedere gli endpoint API disponibili:

Il terminale visualizza il messaggio "php artisan route:
Elenco dei percorsi dell’applicazione sul terminale.

  1. Avviamo l’applicazione e verifichiamo che tutto funzioni correttamente. Possiamo testare questi endpoint tramite il terminale utilizzando uno strumento come Postman o CURL.

Come impostare il rate limiting in un’applicazione Laravel

Per le applicazioni Laravel sono disponibili diverse tecniche di rate limiting. È possibile bloccare un insieme di indirizzi IP o imporre limiti di richiesta basati sulla durata in base all’indirizzo IP o all’ID utente di un utente. Di seguito, applichiamo ciascuno di questi metodi.

  1. Installiamo il pacchetto Laravel Throttle con il seguente comando:
composer require "graham-campbell/throttle:^10.0"
  1. Possiamo anche apportare ulteriori modifiche alle configurazioni di Laravel Throttle pubblicando il file vendor configurations:
php artisan vendor:publish --provider="GrahamCampbellThrottleThrottleServiceProvider"

Come bloccare gli indirizzi IP

Una delle tecniche di rate limiting permette di bloccare le richieste provenienti da un insieme specifico di indirizzi IP.

  1. Per iniziare, creiamo il middleware necessario:
php artisan make:middleware RestrictMiddleware
  1. Apriamo quindi il file middleware creato app/Http/Middleware/RestrictMiddleware.php e sostituiamo il codice della funzione handle con il frammento seguente. Assicuriamoci di aggiungere use App; all’elenco delle importazioni all’inizio del file.
$restrictedIps = ['127.0.0.1', '102.129.158.0'];
if(in_array($request->ip(), $restrictedIps)){
  App::abort(403, 'Request forbidden');
}
return $next($request);
  1. Nel file app/Http/Kernel.php, creiamo un alias per questa applicazione middleware aggiornando l’array middlewareAliases come segue:
    protected $middlewareAliases = [
    . . .
    'custom.restrict' => AppHttpMiddlewareRestrictMiddleware::class,
    ];
    1. Quindi, applichiamo questo middleware a /restricted-route nel file routes/api.php come segue e facciamo un test:
    Route::middleware(['custom.restrict'])->group(function () {
      Route::get('/restricted-route', [BookController::class, 'getBooks']);
    });

    Se funziona correttamente, questo middleware blocca tutte le richieste provenienti dagli IP dell’array $restrictedIps: 127.0.0.1 e 102.129.158.0. Le richieste provenienti da questi IP restituiscono una risposta 403 Forbidden, come mostrato di seguito:

    L'applicazione Postman restituisce una risposta
    Una risposta 403 Forbidden per l’endpoint /restricted-route GET su Postman

    Come limitare le richieste in base all’indirizzo IP

    Successivamente, possiamo limitare le richieste in base all’indirizzo IP dell’utente.

    1. Applichiamo il middleware Throttle alle route GET e PATCH dell’endpoint /book in routes/api.php:
    Route::middleware(['throttle:minute'])->group(function () {
      Route::get('/book', [BookController::class, 'getBooks']);
    });
    
    Route::middleware(['throttle:5,1'])->group(function () {
      Route::patch('/book', [BookController::class, 'updateBook']);
    });
    1. Dovremo anche aggiornare la funzione configureRateLimiting nel file app/Providers/RouteServiceProvider con il middleware che abbiamo aggiunto alle route precedenti.
    … 
    RateLimiter::for('minute', function (Request $request) {
      return Limit::perMinute(5)->by($request->ip());
    });

    Questa configurazione limita le richieste all’endpoint /book GET a 5 al minuto, come mostrato di seguito.

    L'applicazione Postman restituisce una risposta
    Una risposta “429 Too Many Requests” per l’endpoint /book GET su Postman.

    Come limitare le richieste in base all’ID utente e alle sessioni

    1. Per limitare la velocità utilizzando i parametri user_id e session, aggiorniamo la funzione configureRateLimiting nel file app/Providers/RouteServiceProvider con i seguenti limitatori e variabili aggiuntive:
    ...
    RateLimiter::for('user', function (Request $request) {
      return Limit::perMinute(10)->by($request->user()?->id ?: $request->ip());
    });
    RateLimiter::for('session', function (Request $request) {
      return Limit::perMinute(15)->by($request->session()->get('key') ?: $request->ip());
    });
    1. Infine, applichiamo questo codice alle route /book/{id} GET e /book POST nel file routes/api.php:
    Route::middleware(['throttle:user'])->group(function () {
      Route::get('/book/{id}', [BookController::class, 'getBook']);
    });
    Route::middleware(['throttle:session'])->group(function () {
      Route::post('/book', [BookController::class, 'createBook']);
    });

    Questo codice limita le richieste che utilizzano rispettivamente user_id e session.

    Metodi aggiuntivi in Throttle

    Laravel Throttle offre diversi metodi aggiuntivi per un maggiore controllo sull’implementazione del rate limiting. Questi metodi includono:

    • attempt – Colpisce l’endpoint, incrementa il conteggio degli hit e restituisce un booleano che indica se il limite di hit configurato è stato superato.
    • hit – Colpisce il Throttle, incrementa il conteggio degli hit e restituisce $this per abilitare un’altra chiamata al metodo (opzionale).
    • clear – Azzera il conteggio del Throttle e restituisce $this in modo da poter effettuare un’altra chiamata al metodo, se lo desideri.
    • count – Restituisce il numero totale di accessi al Throttle.
    • check – Restituisce un booleano che indica se il limite di hit del Throttle è stato superato.
    1. Per esplorare il rate limiting con questi metodi, creiamo un’applicazione middleware chiamata CustomMiddleware utilizzando il comando seguente:
    php artisan make:middleware CustomMiddleware
    1. Quindi, aggiungiamo i seguenti file di importazione al file middleware appena creato in app/Http/Middleware/CustomMiddleware.php:
    use GrahamCampbellThrottleFacadesThrottle;
    use App;
    1. Quindi, sostituiamo il contenuto del metodo handle con il seguente frammento di codice:
    $throttler = Throttle::get($request, 5, 1);
    Throttle::attempt($request);
    if(!$throttler->check()){
      App::abort(429, 'Too many requests');
    }
    return $next($request);
    1. Nel file app/Http/Kernel.php, creiamo un alias per questa applicazione middleware aggiornando l’array middlewareAliases come segue.
    protected $middlewareAliases = [
    . . .
    'custom.throttle' => AppHttpMiddlewareCustomMiddleware::class, 
    ];
    1. Quindi, applichiamo questo middleware a /custom-route nel file routes/api.php:
    Route::middleware(['custom.throttle'])->group(function () {
      Route::get('/custom-route', [BookController::class, 'getBooks']);
    });

    Il middleware personalizzato appena implementato controlla se il limite di strozzatura è stato superato utilizzando il metodo check. Se il limite viene superato, risponde con un errore 429. Altrimenti, permette alla richiesta di continuare.

    Come distribuire l’applicazione sul server Kinsta

    Ora che abbiamo esplorato come implementare il rate limiting in un’applicazione Laravel, distribuiamo l’applicazione sul server Kinsta per renderla accessibile a livello globale.

    1. Iniziamo inviando il codice aggiornato a GitHub, GitLab o Bitbucket.
    2. Dalla dashboard di Kinsta, clicchiamo sul pulsante Aggiungi servizio e selezioniamo Applicazione dall’elenco. Colleghiamo l’account Git all’account Kinsta e selezioniamo il repository corretto da distribuire.
    3. In Dettagli di base, diamo un nome all’applicazione e scegliamo il data center che preferiamo. Inoltre, assicuriamoci di aver aggiunto le variabili d’ambiente necessarie all’applicazione. Queste corrispondono alle variabili presenti nel file .env locale: le variabili di configurazione di APP_KEY e del database.

    La sezione
    Dettagli sull’applicazione su MyKinsta.

    1. Clicchiamo sul pulsante Continua per selezionare le variabili dell’ambiente di compilazione. Possiamo lasciare i valori predefiniti, poiché Kinsta compila automaticamente i parametri necessari.
    2. Nella scheda Processi, possiamo lasciare i valori predefiniti o inserire un nome per il processo. In questa scheda possiamo anche selezionare le dimensioni dei pod e delle istanze.
    3. Infine, la scheda Pagamento mostra un riepilogo delle selezioni. Aggiungiamo l’opzione di pagamento che preferiamo per completare il processo.
    4. Una volta completato, clicchiamo sulla scheda Applicazioni per visualizzare l’elenco delle applicazioni distribuite.
    5. Clicchiamo sul nome dell’applicazione per visualizzarne i dettagli di distribuzione, come mostrato di seguito. Possiamo utilizzare l’URL dell’applicazione per accedervi.

    La scheda MyKinsta "Distribuzioni" visualizza i dettagli delle applicazioni distribuite..
    Dettagli della distribuzione sulla dashboard di MyKinsta.

    Come testare l’applicazione

    1. Per testare l’applicazione in locale, usiamo il comando php artisan serve.

    Questo comando rende accessibile il browser dell’applicazione all’indirizzo http://localhost:8000. Da qui possiamo testare gli endpoint dell’API a cui abbiamo implementato il rate limiting effettuando chiamate ripetute per attivare la funzionalità di rate limiting.

    Il server Kinsta visualizza una risposta Access Forbidden perché non abbiamo aggiunto i dettagli di configurazione che indicano a Kinsta come servire l’applicazione. Aggiungiamo ora questi dettagli.

    1. Creiamo un file .htaccess nella directory principale dell’applicazione e aggiungiamo il seguente codice al file:
     <IfModule mod_rewrite.c>
      RewriteEngine On
      RewriteRule ^(.*)$ public/$1 [L]
    </IfModule>
    1. Effettuiamo il push di queste modifiche su GitHub e Kinsta effettuerà il deploy automatico per applicare la modifica.
    2. Ora, apriamo l’applicazione utilizzando l’URL fornito e assicuriamoci di visualizzare la pagina di benvenuto di Laravel.

    Ora possiamo testare gli endpoint dell’API su cui abbiamo implementato il rate limiting utilizzando Postman, effettuando chiamate ripetute fino a raggiungere il limite configurato. Dopo aver superato il limite, riceveremo una risposta 429 Too Many Requests.

    Riepilogo

    L’integrazione di funzionalità di rate limiting in un’API di Laravel aiuta a controllare la velocità con cui gli utenti consumano le risorse di un’applicazione. Il rate limiting aiuta a fornire un’esperienza affidabile agli utenti, senza incorrere in sovra e sottoutilizzi. Inoltre, garantisce che l’infrastruttura sottostante l’applicazione rimanga funzionale ed efficiente.

    Date un’occhiata al blog di Kinsta per scoprire altri concetti interessanti su Laravel e altre tecnologie web. I servizi di hosting convenienti e senza interruzioni sono altamente raccomandati per le esigenze della vostra applicazione e del vostro team.

Marcia Ramos Kinsta

Sono l'Editorial Team Lead di Kinsta. Sono un'appassionata di open source e amo il coding. Con più di 7 anni di esperienza in scrittura tecnica e di editing per l'industria tecnologica, amo collaborare con le persone per creare contenuti chiari e concisi e migliorare i flussi di lavoro.