ip(); $ua = strtolower($request->userAgent() ?? ''); $path = '/' . ltrim($request->path(), '/'); // index.php önekini normalize et (/index.php/stream/seg -> /stream/seg) if (str_starts_with($path, '/index.php/')) { $path = '/' . substr($path, 11); } // Skip paths foreach (self::SKIP_PATHS as $skip) { if (str_starts_with($path, $skip)) { return $next($request); } } // Güvenilir IP (Google vb.) — tüm kontrolleri atla foreach (self::TRUSTED_IP_PREFIXES as $prefix) { if (str_starts_with($ip, $prefix)) { return $next($request); } } // Manuel engelli IP kontrolü (Sadece yöneticinin manuel engellediği IP'ler) if ($this->isBlockedIp($ip)) { $this->logBot($ip, $request->userAgent(), $path, $request->method(), 'ip_blocked', 'blocked_ip'); return response('Erişim engellendi.', 403); } // UA boşsa bot olarak işaretle if (empty($ua)) { $request->attributes->set('is_bot', true); $request->attributes->set('bot_type', 'noua'); $this->logBot($ip, '', $path, $request->method(), 'allowed', 'no_ua'); return $next($request); } // Kötü bot mu? foreach (self::BAD_BOTS as $pattern) { if (str_contains($ua, $pattern)) { $this->logBot($ip, $request->userAgent(), $path, $request->method(), 'blocked', $pattern); return response('', 403); } } // İyi bot mu? foreach (self::GOOD_BOTS as $pattern) { if (str_contains($ua, $pattern)) { $request->attributes->set('is_bot', true); $request->attributes->set('bot_type', 'good'); // İyi botlar için rate limit (dakikada 120) if ($this->isRateLimited($ip, 120, 'good_bot')) { return response('', 429); } return $next($request); } } // Generic araç mı? foreach (self::GENERIC_BOTS as $pattern) { if (str_contains($ua, $pattern)) { $request->attributes->set('is_bot', true); $request->attributes->set('bot_type', 'generic'); if ($this->isRateLimited($ip, 20, 'generic')) { $this->logBot($ip, $request->userAgent(), $path, $request->method(), 'rate_limited', $pattern); // 50+ istek → otomatik engelle $count = Cache::get("bot_count_{$ip}", 0); if ($count > 50) { $this->autoBlock($ip, 'Otomatik: dakikada 50+ generic bot isteği'); } return response('', 429); } $this->logBot($ip, $request->userAgent(), $path, $request->method(), 'allowed', $pattern); return $next($request); } } // Normal kullanıcı — genel sayfa istek limiti (dakikada 300 sayfa isteği) if ($this->isRateLimited($ip, 300, 'human')) { $this->logBot($ip, $request->userAgent(), $path, $request->method(), 'rate_limited', 'human_flood'); return response('Çok fazla istek gönderdiniz. Lütfen biraz bekleyin.', 429); } $request->attributes->set('is_bot', false); return $next($request); } private function isBlockedIp(string $ip): bool { return Cache::remember("blocked_ip_{$ip}", 30, function () use ($ip) { try { return DB::table('blocked_ips') ->where('ip', $ip) ->where('auto_blocked', 0) ->where(function ($q) { $q->whereNull('expires_at')->orWhere('expires_at', '>', now()); }) ->exists(); } catch (\Exception) { return false; } }); } private function isRateLimited(string $ip, int $maxPerMinute, string $type): bool { $key = "rl_{$type}_{$ip}"; $count = Cache::get($key, 0); if ($count === 0) { Cache::put($key, 1, 60); } else { Cache::increment($key); } // Bot count ayrı izle Cache::put("bot_count_{$ip}", Cache::get("bot_count_{$ip}", 0) + 1, 60); return $count >= $maxPerMinute; } private function autoBlock(string $ip, string $reason): void { try { DB::table('blocked_ips')->insertOrIgnore([ 'ip' => $ip, 'reason' => $reason, 'auto_blocked' => 1, 'blocked_at' => now(), 'expires_at' => now()->addHours(24), ]); Cache::forget("blocked_ip_{$ip}"); } catch (\Exception) {} } private function logBot(string $ip, ?string $ua, string $path, string $method, string $action, string $botName): void { try { DB::table('analytics_bot_logs')->insert([ 'ip' => $ip, 'user_agent' => mb_substr($ua ?? '', 0, 500), 'path' => mb_substr($path, 0, 500), 'method' => $method, 'action' => $action, 'bot_name' => mb_substr($botName, 0, 100), 'created_at' => now(), ]); } catch (\Exception) {} } }