HoneypotPlus : mon package Laravel pour détecter et bannir les IPs malveillantes

Détectez les bots qui scannent vos applications Laravel sur des chemins sensibles (.env, wp-admin, .git), bannissez-les via Cloudflare et signalez-les à AbuseIPDB. Découvrez HoneypotPlus, un package Laravel événementiel et zéro configuration.

Introduction et contexte

Si vous hébergez des applications Laravel exposées sur internet, vous le savez : les bots ne s'arrêtent jamais. Dès qu'un site est en ligne, il se fait scanner à la recherche de fichiers sensibles : .env, wp-admin, .git, phpinfo.php. Même si votre application n'a rien à voir avec WordPress !

Je cherchais un moyen propre de détecter ces tentatives de reconnaissance, d'en garder une trace, et de réagir automatiquement : bannir l'IP via Cloudflare, la signaler à AbuseIPDB. Rien d'existant ne faisait exactement ça dans l'écosystème Laravel. J'ai donc créé HoneypotPlus.

Attention : HoneypotPlus n'est pas un honeypot de formulaire. Si vous cherchez à protéger vos formulaires contre le spam, regardez plutôt spatie/laravel-honeypot. HoneypotPlus, c'est la détection de reconnaissance réseau sur des chemins sensibles.

Ce que fait le package

Le principe est simple : on déclare une liste de chemins "piège". Toute IP qui tente d'accéder à l'un de ces chemins est immédiatement considérée comme malveillante.

Concrètement, HoneypotPlus :

  • Détecte les accès à des chemins sensibles (.env, wp-admin, .git, etc.) via un middleware
  • Enregistre l'attaque en base de données (IP, chemin, user-agent, méthode HTTP, horodatage)
  • Bannit automatiquement l'IP via l'API Cloudflare Firewall (si les credentials sont présents)
  • Signale l'IP à AbuseIPDB (si la clé API est présente)
  • Nettoie automatiquement les bans expirés via le scheduler Laravel
  • Expose une interface CLI interactive pour gérer les IPs bloquées

Les intégrations Cloudflare et AbuseIPDB sont optionnelles et s'auto-activent dès que les variables d'environnement correspondantes sont présentes. Zéro configuration superflue.

Installation

Installation via Composer :

composer require ilogus/laravel-honeypotplus

Ensuite, lancer la commande d'installation qui publie la config et la migration :

php artisan honeypot-plus:install

Ajouter le middleware dans bootstrap/app.php :

use HoneypotPlus\Middleware\HoneypotPlusMiddleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->append(HoneypotPlusMiddleware::class);
    })
    ->create();

Utiliser append() et non prepend(). Le middleware doit s'exécuter avant le routing Laravel, sinon Laravel renvoie une 404 standard et l'IP malveillante n'est jamais détectée.

Puis configurer les variables d'environnement dans .env :

HONEYPOT_PLUS_ENABLE=true
HONEYPOT_PLUS_LOGGING=true
HONEYPOT_PLUS_BAN_DURATION_HOURS=24

# Cloudflare (optionnel)
HONEYPOT_PLUS_CLOUDFLARE_API_TOKEN=your_token
HONEYPOT_PLUS_CLOUDFLARE_ZONE_ID=your_zone_id

# AbuseIPDB (optionnel)
HONEYPOT_PLUS_ABUSEIPDB_KEY=your_key

Personnaliser les règles honeypot

Dans config/honeypot-plus.php, on peut définir ses propres chemins piège. Le package supporte à la fois les chemins statiques et les patterns regex :

'honeypots' => [
    '/.env',
    '/wp-admin',
    '/.git',
    // Patterns regex (préfixer avec 'regex:')
    'regex:/^\.env\./i',
    'regex:/wp-config\.php$/i',
],

Architecture événementielle

C'est la partie qui m'intéresse le plus dans ce package : l'architecture est entièrement basée sur le système d'événements de Laravel.

Quand une IP déclenche un piège, le middleware dispatch un événement HoneypotAttackDetected :

final class HoneypotAttackDetected
{
    use Dispatchable, SerializesModels;

    public function __construct(
        public string $ip,
        public string $honeypotRule,
        public ?string $userAgent,
        public string $httpMethod,
        public string $pathRequested,
    ) {}
}

Le listener interne HandleHoneypotAttack s'en charge pour enregistrer l'attaque, appeler Cloudflare, et signaler à AbuseIPDB.

Mais le vrai avantage, c'est que vous pouvez écrire vos propres listeners. Si vous voulez envoyer ces événements vers Elasticsearch, Kibana, un canal Slack, un webhook ou n'importe quel autre outil, il suffit de s'abonner à l'événement HoneypotAttackDetected dans votre EventServiceProvider :

use HoneypotPlus\Events\HoneypotAttackDetected;
use App\Listeners\ForwardAttackToElasticsearch;

protected $listen = [
    HoneypotAttackDetected::class => [
        ForwardAttackToElasticsearch::class,
    ],
];

Et votre listener reçoit toutes les informations nécessaires : l'IP, la règle déclenchée, le user-agent, la méthode HTTP et le chemin demandé. Vous faites ensuite ce que vous voulez avec.

class ForwardAttackToElasticsearch implements ShouldQueue
{
    public function handle(HoneypotAttackDetected $event): void
    {
        // Envoyer vers Elasticsearch, Kibana, Slack, etc.
        ElasticsearchClient::index([
            'index' => 'honeypot-attacks',
            'body'  => [
                'ip'           => $event->ip,
                'rule'         => $event->honeypotRule,
                'path'         => $event->pathRequested,
                'user_agent'   => $event->userAgent,
                'method'       => $event->httpMethod,
                'detected_at'  => now()->toIso8601String(),
            ],
        ]);
    }
}

En implémentant ShouldQueue, le traitement se fait de manière asynchrone. Votre réponse HTTP n'est pas bloquée.

Gestion via CLI

Le package expose une commande Artisan interactive pour gérer les IPs bloquées :

php artisan honeypot-plus:manage

Les actions disponibles :

  • Lister les IPs bloquées
  • Bannir une IP manuellement
  • Débannir une IP
  • Afficher les statistiques

On peut aussi utiliser la façade directement dans le code :

use HoneypotPlus\Facades\HoneypotPlus;

HoneypotPlus::ban('192.168.1.1', 24);      // Bannir pour 24h
HoneypotPlus::isBanned('192.168.1.1');     // Vérifier si bannie
HoneypotPlus::unban('192.168.1.1');        // Débannir
$stats = HoneypotPlus::getStats();         // Statistiques

Nettoyage automatique

Le scheduler Laravel est configuré automatiquement. Les bans expirés sont nettoyés régulièrement sans aucune intervention manuelle. Pour vérifier :

php artisan schedule:list

Ce que j'en retiens

Ce package est avant tout un outil pratique que j'utilise sur mes propres projets. La décision de passer par le système d'événements de Laravel plutôt qu'un traitement monolithique était délibérée : ça rend le package extensible sans avoir à toucher à son code source.

Si vous avez des applications Laravel exposées, c'est le genre de chose qu'on installe une fois et qu'on oublie, jusqu'au jour où on regarde les stats et qu'on réalise combien de bots tournent en permanence.

Le code source est disponible sur GitHub. Les contributions et retours sont les bienvenus.