isIPsafe
Laravel logo

Laravel

Block listed addresses with middleware and report failed logins from an event listener.

Two pieces: middleware that turns away listed addresses on the routes you choose (login, registration, checkout), and a listener that reports addresses behind repeated failed logins. Both use Laravel's HTTP client and cache.

Configuration

In config/services.php:

'isipsafe' => [
    'url' => env('ISIPSAFE_URL', 'https://isipsafe.com'),
    'key' => env('ISIPSAFE_KEY'),
],

Block listed addresses

app/Http/Middleware/BlockListedIps.php:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;

class BlockListedIps
{
    public function handle(Request $request, Closure $next)
    {
        $ip = $request->ip();
        $score = Cache::remember("isipsafe:$ip", now()->addHour(), function () use ($ip) {
            try {
                return (int) Http::timeout(3)
                    ->get(config('services.isipsafe.url').'/api/v1/check', ['ip' => $ip])
                    ->json('data.abuseConfidenceScore', 0);
            } catch (\Throwable $e) {
                return 0; // fail open
            }
        });

        abort_if($score >= 75, 403, 'Your network address has a poor reputation. Contact us if this is a mistake.');

        return $next($request);
    }
}

Apply it to the routes that matter, for example Route::post('/login', ...)->middleware(BlockListedIps::class);. Behind a load balancer, configure trusted proxies first, or $request->ip() is the proxy.

Report failed logins

A listener for the framework's Failed event, reporting an address after ten failures in ten minutes. With event discovery (Laravel 11+) it registers itself:

<?php

namespace App\Listeners;

use Illuminate\Auth\Events\Failed;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;

class ReportFailedLogin
{
    public function handle(Failed $event): void
    {
        $ip = request()->ip();
        $key = "isipsafe-fails:$ip";
        Cache::add($key, 0, now()->addMinutes(10));
        if (Cache::increment($key) !== 10) {
            return;
        }
        Http::timeout(5)
            ->withToken(config('services.isipsafe.key'))
            ->asForm()
            ->post(config('services.isipsafe.url').'/api/v1/report', [
                'ip'         => $ip,
                'categories' => 'brute-force,web-app-attack',
                'comment'    => '10 failed logins within 10 minutes on a Laravel application',
            ]);
    }
}

Need a hand?

If something here does not work with your version of Laravel, or you have improved it, tell us. We update these pages with what people send.

Laravel and its logo are trademarks of their respective owners. isipsafe is not affiliated with or endorsed by them.