eventDispatcher = EventDispatcher::getInstance(); $this->cache = Cache::getInstance(); $this->logger = Logger::getInstance(); $this->publicKeyPath = __DIR__ . '/../../../security/keys/public.pem'; $this->privateKeyPath = __DIR__ . '/../../../security/keys/private.pem'; $this->loadSettings(); } /** * Singleton-Instanz abrufen */ public static function getInstance() { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } /** * Einstellungen laden */ private function loadSettings() { try { $conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); $stmt = $conn->prepare(' SELECT setting_key, setting_value FROM ws_security_settings WHERE active = 1 '); $stmt->execute(); $settings = $stmt->fetchAllAssociative(); foreach ($settings as $setting) { switch ($setting['setting_key']) { case 'enabled': $this->enabled = (bool)$setting['setting_value']; break; case 'code_signing_enabled': $this->codeSigningEnabled = (bool)$setting['setting_value']; break; case 'malware_scanning_enabled': $this->malwareScanningEnabled = (bool)$setting['setting_value']; break; case 'sandbox_enabled': $this->sandboxEnabled = (bool)$setting['setting_value']; break; } } } catch (Exception $e) { $this->logger->error('Security-Einstellungen laden Fehler', [ 'error' => $e->getMessage() ]); } } /** * Code signieren */ public function signCode($filePath, $moduleName) { if (!$this->codeSigningEnabled) { return ['success' => true, 'signed' => false]; } try { // Datei-Hash erstellen $fileHash = hash_file('sha256', $filePath); // Signatur erstellen $signature = $this->createSignature($fileHash, $moduleName); // Signatur in Datei einbetten $this->embedSignature($filePath, $signature); // Signatur in Datenbank speichern $this->saveSignature($moduleName, $filePath, $signature, $fileHash); $this->logger->info('Code signiert', [ 'module_name' => $moduleName, 'file_path' => $filePath, 'signature' => substr($signature, 0, 32) . '...' ]); return ['success' => true, 'signed' => true, 'signature' => $signature]; } catch (\Exception $e) { $this->logger->error('Code-Signierung Fehler', [ 'module_name' => $moduleName, 'file_path' => $filePath, 'error' => $e->getMessage() ]); return ['success' => false, 'error' => $e->getMessage()]; } } /** * Code-Signatur erstellen */ private function createSignature($fileHash, $moduleName) { if (!file_exists($this->privateKeyPath)) { throw new \Exception('Private Key nicht gefunden'); } $privateKey = openssl_pkey_get_private(file_get_contents($this->privateKeyPath)); if (!$privateKey) { throw new \Exception('Private Key ungültig'); } $data = $fileHash . '|' . $moduleName . '|' . time(); $signature = ''; $result = openssl_sign($data, $signature, $privateKey, OPENSSL_ALGO_SHA256); openssl_free_key($privateKey); if (!$result) { throw new \Exception('Signatur-Erstellung fehlgeschlagen'); } return base64_encode($signature); } /** * Signatur in Datei einbetten */ private function embedSignature($filePath, $signature) { $content = file_get_contents($filePath); // Signatur-Kommentar hinzufügen $signatureComment = "\n// SIGNATURE: " . $signature . "\n"; // Am Ende der Datei hinzufügen $content .= $signatureComment; file_put_contents($filePath, $content); } /** * Signatur in Datenbank speichern */ private function saveSignature($moduleName, $filePath, $signature, $fileHash) { try { $conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); $stmt = $conn->prepare(' INSERT INTO ws_code_signatures ( module_name, file_path, signature, file_hash, created_at ) VALUES (?, ?, ?, ?, NOW()) ON DUPLICATE KEY UPDATE signature = ?, file_hash = ?, updated_at = NOW() '); $stmt->execute([ $moduleName, $filePath, $signature, $fileHash, $signature, $fileHash ]); } catch (Exception $e) { $this->logger->error('Signatur speichern Fehler', [ 'error' => $e->getMessage() ]); } } /** * Code-Signatur verifizieren */ public function verifySignature($filePath, $moduleName) { if (!$this->codeSigningEnabled) { return ['success' => true, 'verified' => true]; } try { // Signatur aus Datei extrahieren $signature = $this->extractSignature($filePath); if (!$signature) { return ['success' => false, 'error' => 'Keine Signatur gefunden']; } // Datei-Hash erstellen (ohne Signatur) $content = file_get_contents($filePath); $content = preg_replace('/\/\/ SIGNATURE: .*$/m', '', $content); $fileHash = hash('sha256', $content); // Signatur verifizieren $verified = $this->verifySignatureData($signature, $fileHash, $moduleName); if ($verified) { $this->logger->info('Code-Signatur verifiziert', [ 'module_name' => $moduleName, 'file_path' => $filePath ]); return ['success' => true, 'verified' => true]; } else { return ['success' => false, 'error' => 'Signatur ungültig']; } } catch (\Exception $e) { $this->logger->error('Code-Signatur-Verifikation Fehler', [ 'module_name' => $moduleName, 'file_path' => $filePath, 'error' => $e->getMessage() ]); return ['success' => false, 'error' => $e->getMessage()]; } } /** * Signatur aus Datei extrahieren */ private function extractSignature($filePath) { $content = file_get_contents($filePath); if (preg_match('/\/\/ SIGNATURE: (.+)$/m', $content, $matches)) { return $matches[1]; } return null; } /** * Signatur-Daten verifizieren */ private function verifySignatureData($signature, $fileHash, $moduleName) { if (!file_exists($this->publicKeyPath)) { throw new \Exception('Public Key nicht gefunden'); } $publicKey = openssl_pkey_get_public(file_get_contents($this->publicKeyPath)); if (!$publicKey) { throw new \Exception('Public Key ungültig'); } $signatureData = base64_decode($signature); $data = $fileHash . '|' . $moduleName . '|' . time(); $result = openssl_verify($data, $signatureData, $publicKey, OPENSSL_ALGO_SHA256); openssl_free_key($publicKey); return $result === 1; } /** * Malware-Scan durchführen */ public function scanForMalware($filePath, $moduleName) { if (!$this->malwareScanningEnabled) { return ['success' => true, 'clean' => true]; } try { // Event auslösen $this->eventDispatcher->dispatch('security.malware.scan.before', [ 'file_path' => $filePath, 'module_name' => $moduleName ]); $threats = []; // Datei-Inhalt scannen $content = file_get_contents($filePath); // Bekannte Malware-Patterns prüfen $threats = array_merge($threats, $this->scanForPatterns($content)); // PHP-Code-Analyse $threats = array_merge($threats, $this->analyzePhpCode($content)); // Datei-Hash prüfen $threats = array_merge($threats, $this->checkFileHash($filePath)); // Sandbox-Test if ($this->sandboxEnabled) { $threats = array_merge($threats, $this->sandboxTest($filePath)); } $isClean = empty($threats); // Scan-Ergebnis speichern $this->saveScanResult($moduleName, $filePath, $threats, $isClean); // Event auslösen $this->eventDispatcher->dispatch('security.malware.scan.after', [ 'file_path' => $filePath, 'module_name' => $moduleName, 'threats' => $threats, 'is_clean' => $isClean ]); if ($isClean) { $this->logger->info('Malware-Scan abgeschlossen - Datei ist sauber', [ 'module_name' => $moduleName, 'file_path' => $filePath ]); } else { $this->logger->warning('Malware-Scan abgeschlossen - Bedrohungen gefunden', [ 'module_name' => $moduleName, 'file_path' => $filePath, 'threats' => $threats ]); } return [ 'success' => true, 'clean' => $isClean, 'threats' => $threats ]; } catch (\Exception $e) { $this->logger->error('Malware-Scan Fehler', [ 'module_name' => $moduleName, 'file_path' => $filePath, 'error' => $e->getMessage() ]); return ['success' => false, 'error' => $e->getMessage()]; } } /** * Pattern-basierte Malware-Erkennung */ private function scanForPatterns($content) { $threats = []; $malwarePatterns = [ 'eval\s*\(' => 'Eval-Funktion gefunden', 'exec\s*\(' => 'Exec-Funktion gefunden', 'system\s*\(' => 'System-Funktion gefunden', 'shell_exec\s*\(' => 'Shell-Exec-Funktion gefunden', 'passthru\s*\(' => 'Passthru-Funktion gefunden', 'base64_decode\s*\(' => 'Base64-Decode gefunden', 'gzinflate\s*\(' => 'Gzinflate-Funktion gefunden', 'str_rot13\s*\(' => 'ROT13-Verschlüsselung gefunden', 'file_get_contents\s*\(\s*[\'"]https?://' => 'Externe URL-Zugriffe gefunden', 'curl_exec\s*\(' => 'CURL-Exec gefunden', 'fopen\s*\(\s*[\'"]https?://' => 'Externe Datei-Öffnung gefunden' ]; foreach ($malwarePatterns as $pattern => $description) { if (preg_match('/' . $pattern . '/i', $content)) { $threats[] = [ 'type' => 'pattern', 'description' => $description, 'severity' => 'medium' ]; } } return $threats; } /** * PHP-Code-Analyse */ private function analyzePhpCode($content) { $threats = []; // AST-Analyse (vereinfacht) $tokens = token_get_all($content); foreach ($tokens as $token) { if (is_array($token)) { $tokenType = $token[0]; $tokenValue = $token[1]; // Gefährliche Funktionen prüfen $dangerousFunctions = ['eval', 'exec', 'system', 'shell_exec', 'passthru']; if ($tokenType === T_STRING && in_array(strtolower($tokenValue), $dangerousFunctions)) { $threats[] = [ 'type' => 'dangerous_function', 'description' => 'Gefährliche Funktion gefunden: ' . $tokenValue, 'severity' => 'high' ]; } } } return $threats; } /** * Datei-Hash prüfen */ private function checkFileHash($filePath) { $threats = []; try { $conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); $fileHash = hash_file('sha256', $filePath); // Bekannte Malware-Hashes prüfen $stmt = $conn->prepare(' SELECT description FROM ws_malware_hashes WHERE hash = ? AND active = 1 '); $stmt->execute([$fileHash]); $malwareHashes = $stmt->fetchAllAssociative(); foreach ($malwareHashes as $malware) { $threats[] = [ 'type' => 'known_malware', 'description' => 'Bekannte Malware: ' . $malware['description'], 'severity' => 'critical' ]; } } catch (Exception $e) { $this->logger->error('Datei-Hash-Prüfung Fehler', [ 'error' => $e->getMessage() ]); } return $threats; } /** * Sandbox-Test */ private function sandboxTest($filePath) { $threats = []; try { // Isolierte Umgebung erstellen $sandboxDir = __DIR__ . '/../../../security/sandbox/'; if (!is_dir($sandboxDir)) { mkdir($sandboxDir, 0755, true); } $sandboxFile = $sandboxDir . 'test_' . uniqid() . '.php'; copy($filePath, $sandboxFile); // Sandbox-Ausführung (sehr eingeschränkt) $output = []; $returnCode = 0; // Nur Syntax-Check exec('php -l ' . escapeshellarg($sandboxFile) . ' 2>&1', $output, $returnCode); if ($returnCode !== 0) { $threats[] = [ 'type' => 'syntax_error', 'description' => 'PHP-Syntax-Fehler: ' . implode(' ', $output), 'severity' => 'medium' ]; } // Sandbox-Datei löschen unlink($sandboxFile); } catch (\Exception $e) { $threats[] = [ 'type' => 'sandbox_error', 'description' => 'Sandbox-Test fehlgeschlagen: ' . $e->getMessage(), 'severity' => 'low' ]; } return $threats; } /** * Scan-Ergebnis speichern */ private function saveScanResult($moduleName, $filePath, $threats, $isClean) { try { $conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); $stmt = $conn->prepare(' INSERT INTO ws_security_scans ( module_name, file_path, threats, is_clean, scan_date ) VALUES (?, ?, ?, ?, NOW()) '); $stmt->execute([ $moduleName, $filePath, json_encode($threats), $isClean ? 1 : 0 ]); } catch (Exception $e) { $this->logger->error('Scan-Ergebnis speichern Fehler', [ 'error' => $e->getMessage() ]); } } /** * Security-Scan für Modul durchführen */ public function scanModule($moduleName, $modulePath) { $results = [ 'signature' => null, 'malware' => null, 'overall_clean' => true ]; // Hauptdatei scannen $mainFile = $modulePath . '/Module.php'; if (file_exists($mainFile)) { // Code signieren $signatureResult = $this->signCode($mainFile, $moduleName); $results['signature'] = $signatureResult; // Malware-Scan $malwareResult = $this->scanForMalware($mainFile, $moduleName); $results['malware'] = $malwareResult; if (!$malwareResult['clean']) { $results['overall_clean'] = false; } } // Alle PHP-Dateien scannen $phpFiles = $this->findPhpFiles($modulePath); foreach ($phpFiles as $file) { $malwareResult = $this->scanForMalware($file, $moduleName); if (!$malwareResult['clean']) { $results['overall_clean'] = false; } } return $results; } /** * PHP-Dateien finden */ private function findPhpFiles($directory) { $files = []; $iterator = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator($directory) ); foreach ($iterator as $file) { if ($file->isFile() && $file->getExtension() === 'php') { $files[] = $file->getPathname(); } } return $files; } /** * Security-Einstellungen speichern */ public function saveSettings($settings) { try { $conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); foreach ($settings as $key => $value) { $stmt = $conn->prepare(' INSERT INTO ws_security_settings ( setting_key, setting_value, active, updated_at ) VALUES (?, ?, 1, NOW()) ON DUPLICATE KEY UPDATE setting_value = ?, updated_at = NOW() '); $stmt->execute([$key, $value, $value]); } // Einstellungen neu laden $this->loadSettings(); $this->logger->info('Security-Einstellungen gespeichert', [ 'settings' => $settings ]); return true; } catch (Exception $e) { $this->logger->error('Security-Einstellungen speichern Fehler', [ 'error' => $e->getMessage() ]); return false; } } /** * Security-System aktivieren/deaktivieren */ public function setEnabled($enabled) { $this->enabled = $enabled; return $this; } /** * Security-System Status prüfen */ public function isEnabled() { return $this->enabled; } /** * Code-Signierung aktivieren/deaktivieren */ public function setCodeSigningEnabled($enabled) { $this->codeSigningEnabled = $enabled; return $this; } /** * Code-Signierung Status prüfen */ public function isCodeSigningEnabled() { return $this->codeSigningEnabled; } /** * Malware-Scanning aktivieren/deaktivieren */ public function setMalwareScanningEnabled($enabled) { $this->malwareScanningEnabled = $enabled; return $this; } /** * Malware-Scanning Status prüfen */ public function isMalwareScanningEnabled() { return $this->malwareScanningEnabled; } /** * Sandbox aktivieren/deaktivieren */ public function setSandboxEnabled($enabled) { $this->sandboxEnabled = $enabled; return $this; } /** * Sandbox Status prüfen */ public function isSandboxEnabled() { return $this->sandboxEnabled; } }