option('threshold'); $timeWindow = (int) $this->option('time-window'); $autoBlock = (bool) $this->option('block'); $this->info("DDoS detectie gestart (threshold: {$threshold} req/IP in {$timeWindow}s)"); $ddosData = $this->getDDoSData(); $suspiciousIps = $this->analyzeTraffic($ddosData, $threshold, $timeWindow); if ($suspiciousIps === []) { $this->line('Geen verdachte activiteit gedetecteerd.'); return Command::SUCCESS; } $this->handleSuspiciousIps($suspiciousIps, $alertService, $autoBlock); return Command::SUCCESS; } private function getDDoSData(): array { $data = Cache::get(self::CACHE_KEY_DDOS_TRACKING, []); if (empty($data)) { $data = $this->getManualTrackingData(); } return $data; } private function getManualTrackingData(): array { $tracking = Cache::get('manual_ip_tracking', []); foreach ($tracking as $ip => $data) { $tracking[$ip]['requests'] = array_filter( $data['requests'], fn ($timestamp) => $timestamp > time() - self::TRACKING_DURATION_SECONDS, ); $tracking[$ip]['count'] = count($tracking[$ip]['requests']); if ($tracking[$ip]['count'] === 0) { unset($tracking[$ip]); } } Cache::put('manual_ip_tracking', $tracking, self::TRACKING_DURATION_SECONDS); return $tracking; } private function analyzeTraffic(array $data, int $threshold, int $timeWindow): array { $suspicious = []; $cutoffTime = time() - $timeWindow; foreach ($data as $ip => $info) { $recentRequests = array_filter( $info['requests'], fn ($timestamp) => $timestamp > $cutoffTime, ); $requestCount = count($recentRequests); if ($requestCount >= $threshold) { $suspicious[$ip] = [ 'count' => $requestCount, 'time_window' => $timeWindow, 'first_seen' => $recentRequests === [] ? time() : min($recentRequests), 'last_seen' => $recentRequests === [] ? time() : max($recentRequests), ]; } } return $suspicious; } private function handleSuspiciousIps(array $suspiciousIps, AlertService $alertService, bool $autoBlock): void { $blockedIps = Cache::get(self::CACHE_KEY_BLOCKED_IPS, []); $newBlocks = []; foreach ($suspiciousIps as $ip => $details) { if (! $this->isValidIp($ip)) { continue; } $this->error("VERDACHTE IP GEDETECTEERD: {$ip}"); $this->table( ['Metric', 'Value'], [ ['Requests', $details['count']], ['Time Window', $details['time_window'] . 's'], ['First Seen', date('H:i:s', $details['first_seen'])], ['Last Seen', date('H:i:s', $details['last_seen'])], ], ); if ($autoBlock && ! in_array($ip, $blockedIps)) { $this->blockIp($ip); $blockedIps[] = $ip; $newBlocks[] = $ip; } } $totalRequests = array_sum(array_column($suspiciousIps, 'count')); $uniqueIps = count($suspiciousIps); $alertService->sendDDoSDetected([ 'total_requests' => $totalRequests, 'unique_ips' => $uniqueIps, 'time_window' => array_first($suspiciousIps)['time_window'], 'suspicious_ips' => array_keys($suspiciousIps), 'auto_blocked' => $newBlocks, ]); if ($newBlocks !== []) { $this->info('Automatisch geblokkeerd: ' . implode(', ', $newBlocks)); } Cache::put(self::CACHE_KEY_BLOCKED_IPS, $blockedIps, 3600); } private function isValidIp(string $ip): bool { return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false; } private function blockIp(string $ip): void { if (! $this->isValidIp($ip)) { Log::warning("Attempted to block invalid IP: {$ip}"); return; } try { $escapedIp = escapeshellarg($ip); exec("iptables -A INPUT -s {$escapedIp} -j DROP 2>/dev/null"); Log::warning("IP blocked due to DDoS detection: {$ip}"); $this->warn("IP {$ip} geblokkeerd via iptables."); } catch (\Exception $e) { Log::error("Failed to block IP {$ip}: " . $e->getMessage()); $this->error("Kon IP {$ip} niet blokkeren: " . $e->getMessage()); } } public static function trackRequest(string $ip): void { if (! filter_var($ip, FILTER_VALIDATE_IP)) { return; } $tracking = Cache::get('manual_ip_tracking', []); if (! isset($tracking[$ip])) { $tracking[$ip] = ['requests' => [], 'count' => 0]; } $tracking[$ip]['requests'][] = time(); $tracking[$ip]['count']++; Cache::put('manual_ip_tracking', $tracking, 300); } public static function getBlockedIps(): array { return Cache::get(self::CACHE_KEY_BLOCKED_IPS, []); } public static function clearBlockedIp(string $ip): void { if (! filter_var($ip, FILTER_VALIDATE_IP)) { return; } $blocked = Cache::get(self::CACHE_KEY_BLOCKED_IPS, []); $blocked = array_filter($blocked, fn ($blockedIp) => $blockedIp !== $ip); Cache::put(self::CACHE_KEY_BLOCKED_IPS, array_values($blocked), 3600); $escapedIp = escapeshellarg($ip); exec("iptables -D INPUT -s {$escapedIp} -j DROP 2>/dev/null"); } }