Skip to content
Aggiungere Turnstile di Cloudflare a Laravel Filament

Aggiungere Turnstile di Cloudflare a Laravel Filament

By Pier Luigi Papeschi

Giorni fa abbiamo visto come integrare Google reCaptcha V3 alla pagina di login di Filament, clicca qui per vedere come si fa. Oggi invece vediamo come fare una cosa analoga con Cloudflare Turnstile. Se ti chiedi perché usare un sistema alternativo a reCaptcha, ti rispondo brevemente: Cloudflare è un gigante del web specializzato in DNS / CDN (Content Delivery Network) e sicurezza web, quindi è forse (non esistono certezze, ma solo prove empiriche che cercano di dimostrare l’efficacia dei vari strumenti) più adeguato alle crescenti difficoltà che stiamo avendo nella protezione dei pannelli di amministrazione dei siti web a causa della maggiore “furbizia” introdotta dall’AI da parte degli hacker (ecco a voi il nuovo acronimo AIH aritificial intelligence hackers): Addio!

Cloudflare Turnstile

Per prima cosa si deve creare un account gratuito su Cloudflare: cloudeflare.com

Aggiungiamo un widget inserendo un nome che serve a noi per identificarlo nel caso se ne creino diversi, e una serie di domini che vogliamo proteggere.

Fatto questo ci vengono date le chiavi del sito e segreta che ci serviranno successivamente, quindi salvale e inseriscile in fondo al file .env:

TURNSTILE_SITE_KEY=xxxxxxxx
TURNSTILE_SECRET_KEY=xxxxxxxx

Rendiamo accessibili queste chiavi inserendo questo codice in fondo al file config/services.php, prima della chiusura dell’array:

'turnstile' => [
    'site_key' => env('TURNSTILE_SITE_KEY'),
    'secret_key' => env('TURNSTILE_SECRET_KEY'),
],

Adattare Filament

Per prima cosa aggiungiamo la libreria Javascript di Cloudflare nella parte HEAD della pagina login di Filament, quindi apriamo app/Providers/Filament/AdminPanelProvider.php e alla variabile $panel aggiungiamo questo metodo:

->renderHook(
    PanelsRenderHook::HEAD_END,
    fn (): string => '<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>'
)

In alto nel solito file aggiungiamo use Filament\View\PanelsRenderHook;

Per intervenire nelle pagine di Filament interne, dobbiamo esporle e aggiungere quanto necessario.

php artisan make:filament-page Auth/Login

Questo comando andrà a creare il file Login.php dentro app/Filament/Admin/Pages/Auth (se così non fosse deve essere questa la struttura delle cartelle altrimenti il sistema si confonde). In Login.php mettiamo questo codice:

<?php

namespace App\Filament\Admin\Pages\Auth;

use Filament\Forms\Components\ViewField;
use Filament\Auth\Pages\Login as BaseLogin;
use Filament\Schemas\Schema;
use Illuminate\Support\Facades\Http;
use Filament\Notifications\Notification;
use Filament\Auth\Http\Responses\Contracts\LoginResponse;

class Login extends BaseLogin
{
    public ?string $turnstileToken = null;

    public function form(Schema $schema): Schema
    {
        return parent::form($schema)
            ->components([
                ...parent::form($schema)->getComponents(),

                ViewField::make('turnstile')
                    ->view('filament.forms.turnstile'),
            ]);
    }

    public function authenticate(): ?LoginResponse
    {
        $response = Http::asForm()->post(
            'https://challenges.cloudflare.com/turnstile/v0/siteverify',
            [
                'secret' => config('services.turnstile.secret_key'),
                'response' => $this->turnstileToken,
                'remoteip' => request()->ip(),
            ]
        );

        if (!($response->json()['success'] ?? false)) {

            Notification::make()
                ->title('Verifica anti-bot fallita')
                ->danger()
                ->send();

            return null;
        }

        return parent::authenticate();
    }
}

 

Questa nuova pagina di login non verrà usata da Filament finché non glielo insegniamo andando nel file AdminPanelProvider.php già aperto in precedenza

->login(\App\Filament\Admin\Pages\Auth\Login::class)

La ->login(\App\Filament\Admin\Pages\Auth\Login::class) va al posto di ->login() nel corpo del metodo panel.

Aggiungiamo un componente blade specifico per gestire questo meccanismo nella pagina di login creando questo file resources/views/filament/forms/turnstile.blade.php Poiché la cartella filament/forms non esisterà crea anche le cartelle mancanti. Il contenuto del file è il seguente:

<div wire:ignore>
    <div
        class="cf-turnstile"
        data-sitekey="{{ config('services.turnstile.site_key') }}"
        data-callback="onTurnstileSuccess">
    </div>
</div>

<script>
    function onTurnstileSuccess(token) {
        Livewire.find(
            document.querySelector('[wire\\:id]').getAttribute('wire:id')
        ).set('turnstileToken', token)
    }
</script>

Bonus

Se vogliamo eliminare il badge che invita cliccare per dimostrare che non siamo un bot, aggiungiamo questa proprietà al componente blade di turnsitle (turnstile.blade.php):

data-size="invisible"

Poi dobbiamo abilitare questa versione invisibile (l’analogo del reCaptcha V3) nel pannello di gestione del widget sul sito Cloudflare facendo “Edit” sul widget ed in basse selezionare “Invisible mode”.