rilo-arbabillah/laravel-crowdsec

A lightweight CrowdSec-like WAF protection package for Laravel

Maintainers

Package info

github.com/RiloArbabillah/crowdsec

pkg:composer/rilo-arbabillah/laravel-crowdsec

Statistics

Installs: 34

Dependents: 0

Suggesters: 0

Stars: 3

Open Issues: 0

v1.0.2 2026-04-06 02:06 UTC

This package is auto-updated.

Last update: 2026-04-20 03:16:18 UTC


README

Tests PHP Version License

A lightweight, CrowdSec-like Web Application Firewall (WAF) protection package for Laravel applications. This package provides real-time threat detection and IP blocking based on WAF patterns and behavior analysis.

Release Status

  • Latest stable Packagist release: v1.0.2
  • Stable compatibility: Laravel ^10.0|^11.0|^12.0 with the current hardening and CI updates

Features

  • WAF Pattern Detection: Detects SQL injection, XSS, path traversal, command injection, and 11 more attack types
  • IP Blocking: Temporary IP blocks with automatic expiration and progressive escalation
  • Behavior-based Protection: Rate limiting, brute-force detection, and threat score tracking with auto-decay
  • Caching Layer: Cached blocked IP lookups for high-traffic applications
  • Event System: 4 Laravel events (ThreatDetected, IpBlocked, IpUnblocked, BehaviorThresholdExceeded)
  • Notifications: Email and Slack alerts with severity filtering and rate limiting
  • Honeypot Routes: Trap routes to catch automated scanners
  • Per-Route Rate Limiting: Configurable rate limits via middleware (crowdsec.rate:60,1)
  • Custom Patterns: Register custom detection scenarios at runtime
  • GeoIP Lookup: IP geolocation via ip-api.com with caching
  • REST API: 6 API endpoints for programmatic management (block, unblock, check, stats, events, blocked)
  • Admin Dashboard: Standalone dark theme Blade dashboard (enable via config)
  • SIEM Export: Export events in JSON, CSV, or Syslog (RFC 5424) format
  • CLI Commands: Statistics, cleanup, and export utilities
  • Facade API: Easy programmatic access to all features
  • Auto-migrations: Database tables created automatically
  • CI Pipeline: GitHub Actions with PHP 8.1/8.2/8.3 × Laravel 10.x/11.x/12.x plus a PHPStan quality gate

Requirements

  • PHP ^8.1
  • Laravel ^10.0 or ^11.0 or ^12.0
  • MySQL/PostgreSQL/SQLite (any Laravel-supported database)

Installation

Install the package via Composer:

composer require rilo-arbabillah/laravel-crowdsec

The package will automatically register its service provider and facade.

Configuration

Publish the configuration file to customize detection scenarios and thresholds:

php artisan vendor:publish --tag=crowdsec-config

This will create config/crowdsec-scenarios.php where you can:

  • Enable or disable the package
  • Configure detection patterns for each attack type
  • Adjust behavior thresholds (request limits, 404 limits, login attempts)
  • Set block durations per severity
  • Whitelist IPs that should never be blocked

Notification Channels

CrowdSec can send on-demand security alerts through email and Slack. Mail delivery uses the notifications.recipients list, while Slack delivery requires an incoming webhook URL.

'notifications' => [
    'enabled' => env('CROWDSEC_NOTIFY_ENABLED', false),
    'channels' => ['mail', 'slack'],
    'severity_threshold' => 'high',
    'rate_limit_minutes' => 5,
    'recipients' => ['security@example.com'],
    'slack_webhook_url' => env('CROWDSEC_NOTIFY_SLACK_WEBHOOK_URL', ''),
],
CROWDSEC_NOTIFY_ENABLED=true
CROWDSEC_NOTIFY_CHANNELS=mail,slack
CROWDSEC_NOTIFY_RECIPIENTS=security@example.com,ops@example.com
CROWDSEC_NOTIFY_SLACK_WEBHOOK_URL=https://hooks.slack.com/services/T000/B000/XXXX

If you enable the slack channel without a webhook URL, php artisan crowdsec:doctor will report the configuration as invalid.

Enabling/Disabling the Package

You can toggle the package on or off via configuration:

// config/crowdsec-scenarios.php

return [
    // Enable or disable the entire package
    'enabled' => true,  // Set to false to disable all protection

    // ... rest of configuration
];

When disabled, the middleware will pass all requests through without any filtering or blocking. This is useful for:

  • Development environments where you need to test without WAF interference
  • Debugging specific issues without protection blocking legitimate traffic
  • Temporary maintenance windows

Default Configuration

// config/crowdsec-scenarios.php

return [
    // Enable/disable the package
    'enabled' => true,

    // Whitelist IPs (won't be blocked)
    'whitelist_ips' => [
        '127.0.0.1',
        '::1',
    ],

    // Behavior thresholds
    'behavior' => [
        'request_threshold' => 500,      // requests per minute
        '404_threshold' => 15,           // 404s per minute
        'login_threshold' => 5,          // login attempts per minute
        'threat_score_threshold' => 50,
        'block_duration' => 240,         // 4 hours
        'severity' => 'high',
    ],

    // Block duration defaults (in minutes)
    'defaults' => [
        'low' => 60,
        'medium' => 240,
        'high' => 720,
        'critical' => 1440,
    ],

    // ... detection patterns
];

Basic Usage

Applying Middleware to Routes

Apply the middleware to individual routes:

use Illuminate\Support\Facades\Route;

Route::middleware(['crowdsec'])->group(function () {
    Route::get('/admin', function () {
        // Protected route
    });

    Route::post('/login', [AuthController::class, 'login']);
});

Applying Middleware Globally

Add the middleware to your HTTP kernel for global protection:

// app/Http/Kernel.php

protected $middlewareAliases = [
    // ...
    'crowdsec' => \RiloArbabillah\LaravelCrowdSec\Http\Middleware\CrowdSecProtection::class,
];

Then apply to routes or route groups:

// Protect all web routes
Route::middleware(['crowdsec'])->group(base_path('routes/web.php'));

// Protect API routes
Route::middleware(['api', 'crowdsec'])->group(base_path('routes/api.php'));

Skipping Middleware for Certain Routes

// Disable for health check endpoints
Route::middleware(['crowdsec'])->group(function () {
    Route::get('/admin', function () {
        // Protected
    });
});

Route::get('/health', function () {
    // Not protected
})->withoutMiddleware(['crowdsec']);

Programmatic Usage

Use the CrowdSec facade for programmatic control:

Check if IP is Blocked

use RiloArbabillah\LaravelCrowdSec\Facades\CrowdSec;

$ip = request()->ip();

if (CrowdSec::isBlocked($ip)) {
    abort(403, 'Your IP has been blocked');
}

Manually Block an IP

use RiloArbabillah\LaravelCrowdSec\Facades\CrowdSec;

// Block for 60 minutes
CrowdSec::blockIp($request->ip(), 'Manual ban - spam', 60);

// Block for 24 hours (default for critical threats)
CrowdSec::blockIp($request->ip(), 'Suspicious activity', 1440);

Unblock an IP

use RiloArbabillah\LaravelCrowdSec\Facades\CrowdSec;

CrowdSec::unblockIp($ip);

Track Login Attempts

Call this after failed login attempts for brute-force protection:

use RiloArbabillah\LaravelCrowdSec\Facades\CrowdSec;

public function login(Request $request)
{
    if (! Auth::attempt($credentials)) {
        // Track failed attempt
        CrowdSec::trackLoginAttempt($request->ip());

        return back()->withErrors(['email' => 'Invalid credentials']);
    }

    // Reset login attempts on successful login
    // (optional - you could implement this)

    return redirect('/dashboard');
}

Analyze Request for Threats

use RiloArbabillah\LaravelCrowdSec\Facades\CrowdSec;

$threats = CrowdSec::analyzeRequest($request);

if (! empty($threats)) {
    foreach ($threats as $threat) {
        \Log::warning('Security threat detected', [
            'type' => $threat['type'],
            'severity' => $threat['severity'],
            'matched' => $threat['matched'],
        ]);
    }
}

CLI Commands

View Protection Statistics

php artisan crowdsec:stats

Output includes:

  • Active blocked IPs
  • Expired blocked IPs
  • Events today
  • Events this week
  • Top attackers (IP addresses)

For JSON output (useful for monitoring):

php artisan crowdsec:stats --json

Clean Up Expired Bans

Remove expired IP blocks and old security events:

php artisan crowdsec:cleanup

If audit logging is enabled, the cleanup command also prunes audit records older than audit.retention_days.

Cleanup Options

# Preview what would be deleted (no changes)
php artisan crowdsec:cleanup --dry-run

# Clean only expired bans
php artisan crowdsec:cleanup --expired

# Clean only old events (older than 30 days)
php artisan crowdsec:cleanup --old-events

# Clean only old behaviors (older than 7 days)
php artisan crowdsec:cleanup --old-behaviors

Audit log retention is configured in config/crowdsec-scenarios.php:

'audit' => [
    'enabled' => env('CROWDSEC_AUDIT_ENABLED', false),
    'retention_days' => env('CROWDSEC_AUDIT_RETENTION_DAYS', 365),
],

Automated Cleanup in Production

Add to your routes/console.php or set up a scheduled command:

// In routes/console.php
Artisan::command('crowdsec:daily-cleanup', function () {
    $this->call('crowdsec:cleanup', ['--expired' => true]);
})->purpose('Clean up expired bans daily');

Or use Laravel's scheduler:

// In app/Console/Kernel.php
protected function schedule(Schedule $schedule)
{
    $schedule->command('crowdsec:cleanup --expired')->daily();
}

Database

The package creates three tables automatically via migrations:

Table Description
blocked_ips Tracks blocked IPs with expiration and reason
ip_behaviors Tracks per-IP metrics (request count, 404s, login attempts, threat score)
security_events Logs all detected security threats

Tables are created when you run:

php artisan migrate

Detected Threats

The package detects the following attack types:

Threat Type Severity Examples
SQL Injection Critical UNION SELECT, OR 1=1, xp_cmdshell
XSS High <script>, javascript:, onclick=
Path Traversal Critical ../, %2e%2e%2f
Command Injection Critical ;cat, |whoami, `id`
File Inclusion High php://input, data:text/html
PHP Serialization Critical O:16:"MaliciousClass"
Directory Bruteforce Medium .git/config, .env, wp-admin
Header Injection High CRLF injection, Location:
Suspicious User Agent Medium sqlmap, nmap, python-requests
Behavior Threshold High Rate limiting, brute-force

Production Guidelines

1. Monitor Regularly

Run stats command periodically or set up monitoring:

# Check for active threats
php artisan crowdsec:stats

# Export to monitoring system
php artisan crowdsec:stats --json | jq '.events_today'

2. Tune Thresholds for Production

Adjust config/crowdsec-scenarios.php based on your traffic:

'behavior' => [
    'request_threshold' => 1000,    // Increase for high-traffic sites
    '404_threshold' => 20,          // Adjust based on your 404 rate
    'login_threshold' => 3,         // Stricter for login pages
    'threat_score_threshold' => 50,
    'block_duration' => 240,
],

3. Protect Optional Endpoints Before Enabling Them

The REST API, metrics endpoint, and dashboard are disabled by default. When you enable them, CrowdSec now defaults to authenticated middleware so they are not exposed publicly by accident.

'api' => [
    'enabled' => env('CROWDSEC_API_ENABLED', false),
    'middleware' => ['api', 'auth:sanctum'],
],

'metrics' => [
    'enabled' => env('CROWDSEC_METRICS_ENABLED', false),
    'path' => 'crowdsec/metrics',
    'middleware' => ['web', 'auth'],
],

'dashboard' => [
    'enabled' => env('CROWDSEC_DASHBOARD_ENABLED', false),
    'path' => 'crowdsec',
    'middleware' => ['web', 'auth'],
],

Recommended production overrides:

'api' => [
    'enabled' => true,
    'middleware' => ['api', 'auth:sanctum'],
],

'metrics' => [
    'enabled' => true,
    'middleware' => ['signed'],
],

'dashboard' => [
    'enabled' => true,
    'middleware' => ['web', 'auth', 'verified'],
],

Run php artisan crowdsec:doctor after enabling any of these surfaces. The doctor command now fails if the API or dashboard lacks authentication, or if metrics are exposed without auth or signed middleware.

4. Whitelist Internal Services

'whitelist_ips' => [
    '127.0.0.1',
    '::1',
    '10.0.0.0/8',      // Internal network
    '192.168.0.0/16',  // Internal network
    'your-load-balancer-ip',
],

5. Set Up Log Monitoring

Monitor Laravel logs for CrowdSec warnings:

// Log channel configuration
'log' => [
    'driver' => 'daily',
    'path' => storage_path('logs/laravel.log'),
    'level' => 'warning',
],

6. Schedule Regular Cleanup

// In app/Console/Kernel.php
protected function schedule(Schedule $schedule)
{
    // Clean expired bans daily at 3 AM
    $schedule->command('crowdsec:cleanup --expired --old-events --old-behaviors')
             ->daily()
             ->at('03:00');
}

7. Performance Considerations

  • The middleware runs on every request - keep patterns optimized
  • IP whitelist is checked first for performance
  • Behavior tracking uses efficient increment operations
  • Consider caching blocked IPs for very high-traffic sites

Performance Benchmarks

The package includes a benchmark suite (tests/Benchmark) to verify overhead stays minimal.

Run benchmarks:

vendor/bin/phpunit tests/Benchmark --testdox

Typical results (100 iterations average):

Operation Avg Time Target
Whitelist bypass ~0.006ms < 0.5ms ✅
Clean request analysis ~0.038ms < 2ms ✅
Blocked IP check ~0.045ms < 2ms ✅
Threat detection (SQLi) ~0.047ms < 5ms ✅
Multi-threat detection ~0.051ms < 5ms ✅
POST body analysis ~0.159ms < 3ms ✅
Behavior tracking ~0.163ms < 3ms ✅

Note: Results may vary depending on hardware. Benchmarks use SQLite in-memory.

Contributing

Contributions are welcome! Please follow these steps:

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/my-new-feature
  3. Make your changes
  4. Run checks: composer test && composer analyse
  5. Commit your changes: git commit -am 'Add some feature'
  6. Push to the branch: git push origin feature/my-new-feature
  7. Submit a Pull Request

Running Tests

composer test

Running Static Analysis

composer analyse

GitHub Actions now runs both PHPUnit and PHPStan as part of the default CI pipeline. The static analysis gate currently focuses on route files, notification delivery, and the crowdsec:doctor command to keep the check lightweight across the supported Laravel version matrix.

Security

If you discover any security-related issues, please email the maintainer instead of opening an issue.

License

This package is open-sourced software licensed under the MIT license.