Proteggere l'endpoint /livewire/update dai bot con un middleware Laravel


Immagina di aprire BugSnag un lunedì mattina e trovare centinaia di errori in arrivo sull’endpoint /livewire/update. Errori come:

Cannot assign array to property App\Livewire\ExampleComponent::$exampleName of type bool

Diverse proprietà coinvolte, ma sempre lo stesso schema: qualcuno sta inviando un array dove PHP si aspetta un tipo primitivo. È quello che è capitato a noi in Encodia.

Il problema: errori di tipo a raffica su BugSnag

In Encodia abbiamo realizzato più di un’applicazione web utilizzando Livewire. Qualche settimana fa, abbiamo iniziato a ricevere segnalazioni da BugSnag relative a errori di questo tipo:

POST /livewire/update Cannot assign array to property App\Livewire\ExampleComponent::$exampleName of type bool

Decine o centinaia di segnalazioni, relative a proprietà pubbliche Livewire, in cui cambia il nome delle proprietà e il tipo atteso, ma l’errore è sempre simile: nel payload, viene inviato un array per idratare una specifica proprietà, ma dato che quella proprietà è di tipo differente (bool, string, ecc.), PHP restituisce un errore.

Come abbiamo identificato la causa

Ispezionando le chiamate su BugSnag, questo header ha attirato subito la mia attenzione:

"user-agent": "python-requests/2.32.4"

Cercando in rete, salta fuori che si tratta di tentativi di sfruttare la vulnerabilità CVE-2025-54068, presente su installazioni Livewire 3.x fino alla versione v3.6.3. La vulnerabilità consente a un attaccante di eseguire codice arbitrario sul server (Remote Code Execution) manipolando il payload di idratazione dei componenti — da qui gli errori di tipo che vedevamo su BugSnag.

Avevamo già aggiornato le applicazioni oggetto delle segnalazioni; tuttavia, gli errori relativi al tipo sono continuati ad arrivare, anche su applicazioni che usano Livewire 4.x. Il motivo è che gli scanner automatici non verificano la versione di Livewire installata prima di tentare l’attacco: semplicemente martellano tutti gli endpoint /livewire/update che trovano, indipendentemente dalla versione.

La soluzione: un middleware Laravel

Ho aggiunto questo middleware:

<?php

declare(strict_types=1);

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

/**
 * Protegge l'endpoint livewire-xxxxxxxx/update da richieste non inviate da Livewire (es. BOT)
 */
final readonly class ProtectLivewireEndpointMiddleware
{
    public function handle(Request $request, Closure $next): mixed
    {
        if (! $request->routeIs('default-livewire.update')) {
            return $next($request);
        }

        if ($request->header('Content-Type') !== 'application/json') {
            $this->abort('Header application/json mancante');
        }

        if (! $request->hasHeader('X-Livewire')) {
            $this->abort('Header X-Livewire mancante');
        }

        if (! $request->headers->get('referer')) {
            $this->abort('Referer mancante');
        }

        if (! $request->cookies->has(config()->string('session.cookie'))) {
            $this->abort('Sessione Laravel non valida');
        }

        return $next($request);
    }

    private function abort(string $reason): never
    {
        $showDetails = app()->isLocal();

        abort(code: 403, message: $showDetails ? $reason : '');
    }
}

Se la richiesta corrente non coinvolge Livewire, procede oltre. Viceversa, controlla che

  • sia presente l’header X-Livewire
  • sia presente il referer
  • sia presente il cookie di sessione

Se almeno una di queste condizioni non è soddisfatta, ritorna un errore 403 (Forbidden) senza alcun messaggio di spiegazione. Solo in ambiente locale - per rendere più facile un eventuale debug - ritorna il motivo per cui è stato erogato 403.

Il middleware va registrato in modo che venga eseguito prima di \Illuminate\Session\Middleware\StartSession. In questo modo, le richieste BOT vengono bloccate prima ancora che Laravel inizializzi la sessione: nessun overhead inutile, nessun accesso al database per richieste che verranno comunque rifiutate.

Alternativa: bloccare i BOT a livello di web server

Se non si vuole utilizzare un middleware, è anche possibile configurare il web server per bloccare le richieste sospette direttamente a livello Nginx, prima che PHP venga coinvolto.

Ad esempio, per bloccare tutte le richieste a /livewire/update prive dell’header X-Livewire:

location /livewire/update {
    if ($http_x_livewire = "") {
        return 403;
    }

    # resto della configurazione...
}

Il vantaggio è che Nginx respinge la richiesta senza che PHP venga mai avviato. Lo svantaggio è che la logica di validazione è separata dall’applicazione e va mantenuta in sincronia con eventuali cambiamenti futuri all’endpoint.

Conclusione

Il middleware non sostituisce l’aggiornamento di Livewire, ma agisce come un filtro a monte: richieste prive degli header attesi vengono bloccate prima ancora che PHP cominci a processare il payload.

Se usi BugSnag o un sistema analogo, vale la pena controllare se stai ricevendo errori simili: python-requests nell’user-agent è un segnale inequivocabile.