Protokoll #32892

ID32892
Zeitstempel2026-02-15 06:33:49.931394
Clientroot
IP145.224.72.253
Modellclaude-sonnet-4-20250514
Statuscompleted
Tokens3,655 (Input: 382, Output: 3,273)
Dauer74 ms
Request-Zeit2026-02-15 06:33:49.931394
Response-Zeit2026-02-15 06:33:50.005106

Request

{
    "event": "PreToolUse",
    "tool_name": "Edit",
    "tool_input": {
        "file_path": "\/tmp\/BotDetectionService.php",
        "old_string": "    public function unblockIp(string $ip): bool\n    {\n        \/\/ Remove from JSON file\n        if (file_exists($this->blockedIpsFile)) {\n            $blockedIps = json_decode(file_get_contents($this->blockedIpsFile), true) ?? [];\n            unset($blockedIps[$ip]);\n            file_put_contents($this->blockedIpsFile, json_encode($blockedIps, JSON_PRETTY_PRINT));\n        }\n        \n        \/\/ Remove UFW rule\n        try {\n            $ufwCmd = sprintf('sudo ufw delete deny from %s', escapeshellarg($ip));\n            shell_exec($ufwCmd . ' 2>&1');\n            SystemLogger::info('security', 'BotDetectionService', \"UFW rule removed for IP: $ip\");\n            return true;\n        } catch (\\Exception $e) {\n            SystemLogger::error('security', 'BotDetectionService', \"Failed to remove UFW rule for $ip: \" . $e->getMessage());\n            return false;\n        }\n    }",
        "new_string": "    public function unblockIp(string $ip): bool\n    {\n        if (file_exists($this->blockedIpsFile)) {\n            $blockedIps = json_decode(file_get_contents($this->blockedIpsFile), true) ?? [];\n            unset($blockedIps[$ip]);\n            file_put_contents($this->blockedIpsFile, json_encode($blockedIps, JSON_PRETTY_PRINT));\n        }\n\n        SystemLogger::info('security', 'BotDetectionService', \"IP unblocked: $ip\");\n        return true;\n    }",
        "replace_all": false
    }
}

Response

{
    "tool_response": {
        "filePath": "\/tmp\/BotDetectionService.php",
        "oldString": "    public function unblockIp(string $ip): bool\n    {\n        \/\/ Remove from JSON file\n        if (file_exists($this->blockedIpsFile)) {\n            $blockedIps = json_decode(file_get_contents($this->blockedIpsFile), true) ?? [];\n            unset($blockedIps[$ip]);\n            file_put_contents($this->blockedIpsFile, json_encode($blockedIps, JSON_PRETTY_PRINT));\n        }\n        \n        \/\/ Remove UFW rule\n        try {\n            $ufwCmd = sprintf('sudo ufw delete deny from %s', escapeshellarg($ip));\n            shell_exec($ufwCmd . ' 2>&1');\n            SystemLogger::info('security', 'BotDetectionService', \"UFW rule removed for IP: $ip\");\n            return true;\n        } catch (\\Exception $e) {\n            SystemLogger::error('security', 'BotDetectionService', \"Failed to remove UFW rule for $ip: \" . $e->getMessage());\n            return false;\n        }\n    }",
        "newString": "    public function unblockIp(string $ip): bool\n    {\n        if (file_exists($this->blockedIpsFile)) {\n            $blockedIps = json_decode(file_get_contents($this->blockedIpsFile), true) ?? [];\n            unset($blockedIps[$ip]);\n            file_put_contents($this->blockedIpsFile, json_encode($blockedIps, JSON_PRETTY_PRINT));\n        }\n\n        SystemLogger::info('security', 'BotDetectionService', \"IP unblocked: $ip\");\n        return true;\n    }",
        "originalFile": "<?php\n\ndeclare(strict_types=1);\n\nnamespace App\\Services;\n\n\nuse App\\Services\\SystemLogger;\nuse App\\Helpers\\ConfigLoader;\nuse App\\Services\\DatabaseManager;\n\n\/**\n * Bot Detection Service mit fail2ban\/nftables-Integration\n * Blockiert unerwünschte Bots basierend auf MariaDB-Konfiguration\n *\/\nclass BotDetectionService\n{\n    private array $config;\n        private string $blockedIpsFile;\n    private array $requestCounts = [];\n    private RateLimitingService $rateLimiter;\n    private ?GeoIPService $geoIPService = null;\n    private ?UserAgentParser $userAgentParser = null;\n\n\n    \/**\n     * DNS verification domains for search engine bots\n     * Pattern => array of valid domain suffixes\n     *\/\n    private const DNS_VERIFY_DOMAINS = [\n        'Googlebot' => ['googlebot.com', 'google.com'],\n        'FeedFetcher-Google' => ['google.com'],\n        'Google-Read-Aloud' => ['google.com', 'googleusercontent.com'],\n        'Google-InspectionTool' => ['google.com'],\n        'Bingbot' => ['search.msn.com'],\n        'Applebot' => ['applebot.apple.com'],\n        'DuckDuckBot' => ['duckduckgo.com'],\n        'Yandex' => ['yandex.ru', 'yandex.net', 'yandex.com'],\n    ];\n\n\n    private const FAIL2BAN_LOG = '\/var\/log\/apache2\/bot-blocks.log';\n\n    public function __construct()\n    {\n        $configLoader = ConfigLoader::getInstance();\n        \n        \/\/ Load ALL configuration directly from MariaDB - NO FALLBACKS\n        $this->config = [\n            'settings' => [\n                'enabled' => (bool)$configLoader->get('bot.enabled'),\n                'log_blocked_bots' => (bool)$configLoader->get('bot.log_blocked_bots'),\n                'block_duration_minutes' => (int)$configLoader->get('bot.block_duration_minutes'),\n                'max_requests_per_minute' => (int)$configLoader->get('bot.max_requests_per_minute')\n            ],\n            'logging' => [\n                'blocked_attempts' => (bool)$configLoader->get('bot.logging.blocked_attempts'),\n                'log_file' => $configLoader->get('bot.logging.log_file')\n            ],\n            'rate_limiting' => [\n                'enabled' => (bool)$configLoader->get('bot.rate_limiting.enabled')\n            ],\n            'actions' => [\n                'block_request' => (bool)$configLoader->get('bot.actions.block_request'),\n                'return_403' => (bool)$configLoader->get('bot.actions.return_403'),\n                'return_404' => (bool)$configLoader->get('bot.actions.return_404')\n            ],\n            'whitelist' => [\n                'user_agents' => $this->safeJsonDecode($configLoader->get('bot.whitelist.user_agents')),\n                'user_agent_patterns' => $this->safeJsonDecode($configLoader->get('bot.whitelist.user_agent_patterns')),\n                'ips' => $this->safeJsonDecode($configLoader->get('bot.whitelist.ips'))\n            ],\n            'blacklist' => [\n                'user_agents' => $this->safeJsonDecode($configLoader->get('bot.blacklist.user_agents')),\n                'user_agent_patterns' => $this->safeJsonDecode($configLoader->get('bot.blacklist.user_agent_patterns'))\n            ],\n            'geo_blocking' => $this->loadGeoBlockingConfig($configLoader),\n            'unknown_bot' => $this->loadUnknownBotConfig($configLoader)\n        ];\n\n        \/\/ Initialize GeoIPService for geo-blocking\n        if ($this->config['geo_blocking']['enabled']) {\n            try {\n                $this->geoIPService = new GeoIPService();\n            } catch (\\Exception $e) {\n                                SystemLogger::error('security', 'BotDetectionService', \"GeoIPService init failed: \" . $e->getMessage());\n            }\n        }\n\n        \/\/ Initialize UserAgentParser for unknown bot detection\n        if ($this->config['unknown_bot']['enabled']) {\n            $this->userAgentParser = new UserAgentParser();\n        }\n        \n                $basePath = dirname(__DIR__, 2); \/\/ Go up from \/src\/Services to project root\n        $this->blockedIpsFile = $basePath . '\/cache\/blocked_ips.json';\n        \n        \/\/ Rate limiting from MariaDB - NO JSON FALLBACKS\n        $rateLimitConfig = [\n            'enabled' => $this->config['rate_limiting']['enabled'],\n            'primary' => [\n                'requests_per_minute' => (int)$configLoader->get('bot.rate_limiting.primary.requests_per_minute'),\n                'burst_requests' => (int)$configLoader->get('bot.rate_limiting.primary.burst_requests'),\n                'burst_window_seconds' => (int)$configLoader->get('bot.rate_limiting.primary.burst_window_seconds')\n            ],\n            'levels' => $this->safeJsonDecode($configLoader->get('bot.rate_limiting.levels')),\n            'tolerance_multipliers' => $this->safeJsonDecode($configLoader->get('bot.rate_limiting.tolerance_multipliers'))\n        ];\n        \n        $this->rateLimiter = new RateLimitingService($rateLimitConfig);\n        \n        \/\/ Lade bestehende blockierte IPs\n        $this->loadBlockedIps();\n    }\n    \n    \/**\n     * Hauptfunktion: Prüft ob Request blockiert werden soll\n     *\/\n    public function shouldBlockRequest(): bool\n    {\n        if (!$this->config['settings']['enabled']) {\n            return false;\n        }\n        \n        $clientIp = $this->getClientIp();\n        $userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';\n        \n        \/\/ 1. Whitelist-Prüfung (höchste Priorität)\n        if ($this->isWhitelisted($clientIp, $userAgent)) {\n            $this->logIfEnabled(\"Whitelist match for IP: $clientIp, UA: $userAgent\", 'whitelist_matches');\n            return false;\n        }\n\n        \/\/ 2. Geo-Blocking (vor Blacklist, nach Whitelist)\n        $geoBlockResult = $this->checkGeoBlocking($clientIp);\n        if ($geoBlockResult['blocked']) {\n            $this->handleGeoBlock($clientIp, $geoBlockResult);\n            return true;\n        }\n\n        \/\/ 3. Unknown Bot Check (vor Blacklist, nach Geo)\n        $unknownBotResult = $this->checkUnknownBot($userAgent);\n        if ($unknownBotResult['blocked']) {\n            $this->handleUnknownBotBlock($clientIp, $userAgent, $unknownBotResult);\n            return true;\n        }\n\n        \/\/ 4. Blacklist-Prüfung\n        if ($this->isBlacklisted($clientIp, $userAgent)) {\n            $this->blockRequest($clientIp, $userAgent, 'blacklist_match');\n            return true;\n        }\n        \n        \/\/ 5. Advanced Rate Limiting\n        $isAuthenticated = $this->isUserAuthenticated();\n        $rateLimitResult = $this->rateLimiter->isRateLimited($clientIp, $userAgent, $isAuthenticated);\n        \n        if ($rateLimitResult['limited']) {\n            $this->handleRateLimitViolation($clientIp, $userAgent, $rateLimitResult);\n            return true;\n        }\n        \n        return false;\n    }\n    \n    \/**\n     * Whitelist-Prüfung\n     *\/\n    private function isWhitelisted(string $ip, string $userAgent): bool\n    {\n        $whitelist = $this->config['whitelist'];\n\n        \/\/ Exakte User-Agent Matches\n        if (in_array($userAgent, $whitelist['user_agents'] ?? [])) {\n            return true;\n        }\n\n        \/\/ User-Agent Pattern Matches (with DNS verification for search engines)\n        foreach ($whitelist['user_agent_patterns'] ?? [] as $pattern) {\n            if (preg_match('\/' . $pattern . '\/i', $userAgent)) {\n                \/\/ For search engine bots: require DNS verification\n                if (isset(self::DNS_VERIFY_DOMAINS[$pattern])) {\n                    if ($this->verifySearchEngineDNS($ip, $pattern)) {\n                        return true;\n                    }\n                    \/\/ DNS verification failed - don't whitelist, continue checking\n                    continue;\n                }\n                return true;\n            }\n        }\n\n        \/\/ IP-Ranges\n        foreach ($whitelist['ips'] ?? [] as $range) {\n            if ($this->ipInRange($ip, $range)) {\n                return true;\n            }\n        }\n\n        \/\/ Check tracking_whitelist (bleib-mensch verified users)\n        if ($this->isInTrackingWhitelist($ip)) {\n            return true;\n        }\n\n        return false;\n    }\n\n    \/**\n     * Prüft ob IP in tracking_whitelist (bleib-mensch Challenge bestanden)\n     *\/\n    private function isInTrackingWhitelist(string $ip): bool\n    {\n        try {\n            $pdo = DatabaseManager::getConnection('mariadb');\n            $ipHash = hash('sha256', $ip);\n\n            $stmt = $pdo->prepare(\"\n                SELECT 1 FROM tracking_whitelist\n                WHERE ip_hash = :ip_hash\n                AND expires_at > NOW()\n                LIMIT 1\n            \");\n            $stmt->execute([':ip_hash' => $ipHash]);\n\n            return $stmt->fetchColumn() !== false;\n        } catch (\\Exception $e) {\n            SystemLogger::error('security', 'BotDetectionService', \"tracking_whitelist check failed: \" . $e->getMessage());\n            return false;\n        }\n    }\n\n\n\n    \/**\n     * Verify search engine bot via reverse\/forward DNS\n     *\n     * Process:\n     * 1. Reverse DNS: IP → hostname\n     * 2. Check if hostname ends with valid domain for this bot\n     * 3. Forward DNS: hostname → IP (must match original)\n     *\n     * @param string $ip Client IP address\n     * @param string $pattern Bot pattern that matched (e.g., \"Googlebot\")\n     * @return bool True if DNS verification passed\n     *\/\n    private function verifySearchEngineDNS(string $ip, string $pattern): bool\n    {\n        \/\/ Only verify patterns that have DNS domains configured\n        if (!isset(self::DNS_VERIFY_DOMAINS[$pattern])) {\n            return true; \/\/ No DNS verification required for this pattern\n        }\n\n        \/\/ Perform DNS verification\n        $verified = $this->performDNSVerification($ip, $pattern);\n\n        \/\/ Log verification result\n        $status = $verified ? 'PASS' : 'FAIL';\n        SystemLogger::info('security', 'BotDetectionService', \"DNS verification {$status}: IP={$ip}, Pattern={$pattern}\");\n\n        return $verified;\n    }\n\n    \/**\n     * Perform actual DNS verification (reverse + forward lookup)\n     *\/\n    privat... [TRUNCATED-cd9a7abaf41bee0d]",
        "structuredPatch": [
            {
                "oldStart": 729,
                "oldLines": 23,
                "newStart": 729,
                "newLines": 14,
                "lines": [
                    "     ",
                    "     public function unblockIp(string $ip): bool",
                    "     {",
                    "-        \/\/ Remove from JSON file",
                    "         if (file_exists($this->blockedIpsFile)) {",
                    "             $blockedIps = json_decode(file_get_contents($this->blockedIpsFile), true) ?? [];",
                    "             unset($blockedIps[$ip]);",
                    "             file_put_contents($this->blockedIpsFile, json_encode($blockedIps, JSON_PRETTY_PRINT));",
                    "         }",
                    "-        ",
                    "-        \/\/ Remove UFW rule",
                    "-        try {",
                    "-            $ufwCmd = sprintf('sudo ufw delete deny from %s', escapeshellarg($ip));",
                    "-            shell_exec($ufwCmd . ' 2>&1');",
                    "-            SystemLogger::info('security', 'BotDetectionService', \"UFW rule removed for IP: $ip\");",
                    "-            return true;",
                    "-        } catch (\\Exception $e) {",
                    "-            SystemLogger::error('security', 'BotDetectionService', \"Failed to remove UFW rule for $ip: \" . $e->getMessage());",
                    "-            return false;",
                    "-        }",
                    "+",
                    "+        SystemLogger::info('security', 'BotDetectionService', \"IP unblocked: $ip\");",
                    "+        return true;",
                    "     }",
                    "     ",
                    "     public function getConfig(): array"
                ]
            }
        ],
        "userModified": false,
        "replaceAll": false
    }
}
← Vorheriger Zur Liste Nächster →