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.
- 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 matrix
- PHP ^8.1
- Laravel ^10.0 or ^11.0
- MySQL/PostgreSQL/SQLite (any Laravel-supported database)
Install the package via Composer:
composer require rilo-arbabillah/laravel-crowdsecThe package will automatically register its service provider and facade.
Publish the configuration file to customize detection scenarios and thresholds:
php artisan vendor:publish --tag=crowdsec-configThis 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
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
// 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
];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']);
});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'));// Disable for health check endpoints
Route::middleware(['crowdsec'])->group(function () {
Route::get('/admin', function () {
// Protected
});
});
Route::get('/health', function () {
// Not protected
})->withoutMiddleware(['crowdsec']);Use the CrowdSec facade for programmatic control:
use RiloArbabillah\LaravelCrowdSec\Facades\CrowdSec;
$ip = request()->ip();
if (CrowdSec::isBlocked($ip)) {
abort(403, 'Your IP has been blocked');
}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);use RiloArbabillah\LaravelCrowdSec\Facades\CrowdSec;
CrowdSec::unblockIp($ip);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');
}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'],
]);
}
}php artisan crowdsec:statsOutput 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 --jsonRemove expired IP blocks and old security events:
php artisan crowdsec:cleanup# 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-behaviorsAdd 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();
}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 migrateThe 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 |
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'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,
],'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',
],Monitor Laravel logs for CrowdSec warnings:
// Log channel configuration
'log' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'level' => 'warning',
],// 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');
}- 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
The package includes a benchmark suite (tests/Benchmark) to verify overhead stays minimal.
Run benchmarks:
vendor/bin/phpunit tests/Benchmark --testdoxTypical 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.
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-new-feature - Make your changes
- Run tests:
composer test - Commit your changes:
git commit -am 'Add some feature' - Push to the branch:
git push origin feature/my-new-feature - Submit a Pull Request
composer testIf you discover any security-related issues, please email the maintainer instead of opening an issue.
This package is open-sourced software licensed under the MIT license.