security = new Security(); $this->backup = new Backup(); } /** * Security Dashboard */ public function dashboard() { if (!$this->checkAdminSession()) { $this->redirect('/admin/login'); } $securityStatus = $this->getSecurityStatus(); $backupStatus = $this->backup->checkBackupStatus(); $recentEvents = $this->getRecentSecurityEvents(); $rateLimitStats = $this->getRateLimitStatistics(); $this->render('admin/security/dashboard.html.twig', [ 'title' => 'Sicherheits-Dashboard', 'security_status' => $securityStatus, 'backup_status' => $backupStatus, 'recent_events' => $recentEvents, 'rate_limit_stats' => $rateLimitStats ]); } /** * Security Settings */ public function settings() { if (!$this->checkAdminSession()) { $this->redirect('/admin/login'); } if ($_SERVER['REQUEST_METHOD'] === 'POST') { $this->updateSecuritySettings(); } $settings = $this->getSecuritySettings(); $this->render('admin/security/settings.html.twig', [ 'title' => 'Sicherheitseinstellungen', 'settings' => $settings ]); } /** * Backup Management */ public function backup() { if (!$this->checkAdminSession()) { $this->redirect('/admin/login'); } $action = $_GET['action'] ?? ''; switch ($action) { case 'create': $this->createBackup(); break; case 'restore': $this->restoreBackup(); break; case 'download': $this->downloadBackup(); break; case 'delete': $this->deleteBackup(); break; default: $this->showBackupList(); } } /** * Security Logs */ public function logs() { if (!$this->checkAdminSession()) { $this->redirect('/admin/login'); } $page = max(1, intval($_GET['page'] ?? 1)); $limit = 50; $offset = ($page - 1) * $limit; $filters = [ 'level' => $_GET['level'] ?? '', 'event' => $_GET['event'] ?? '', 'date_from' => $_GET['date_from'] ?? '', 'date_to' => $_GET['date_to'] ?? '' ]; $logs = $this->getSecurityLogs($filters, $limit, $offset); $totalLogs = $this->getSecurityLogsCount($filters); $this->render('admin/security/logs.html.twig', [ 'title' => 'Sicherheits-Logs', 'logs' => $logs, 'filters' => $filters, 'pagination' => [ 'page' => $page, 'limit' => $limit, 'total' => $totalLogs, 'pages' => ceil($totalLogs / $limit) ] ]); } /** * Rate Limiting Management */ public function rateLimiting() { if (!$this->checkAdminSession()) { $this->redirect('/admin/login'); } if ($_SERVER['REQUEST_METHOD'] === 'POST') { $this->updateRateLimiting(); } $rateLimits = $this->getRateLimitingConfig(); $statistics = $this->getRateLimitStatistics(); $this->render('admin/security/rate_limiting.html.twig', [ 'title' => 'Rate Limiting', 'rate_limits' => $rateLimits, 'statistics' => $statistics ]); } /** * SSL/TLS Configuration */ public function ssl() { if (!$this->checkAdminSession()) { $this->redirect('/admin/login'); } $sslStatus = $this->getSSLStatus(); $sslConfig = $this->getSSLConfiguration(); if ($_SERVER['REQUEST_METHOD'] === 'POST') { $this->updateSSLConfiguration(); } $this->render('admin/security/ssl.html.twig', [ 'title' => 'SSL/TLS Konfiguration', 'ssl_status' => $sslStatus, 'ssl_config' => $sslConfig ]); } /** * Security Status abrufen */ private function getSecurityStatus() { $status = [ 'ssl_enabled' => isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on', 'security_headers' => $this->checkSecurityHeaders(), 'session_security' => $this->checkSessionSecurity(), 'file_permissions' => $this->checkFilePermissions(), 'database_security' => $this->checkDatabaseSecurity(), 'backup_status' => $this->backup->checkBackupStatus() ]; $status['overall_score'] = $this->calculateSecurityScore($status); return $status; } /** * Security Headers prüfen */ private function checkSecurityHeaders() { $headers = [ 'Strict-Transport-Security' => false, 'X-Content-Type-Options' => false, 'X-Frame-Options' => false, 'X-XSS-Protection' => false, 'Referrer-Policy' => false, 'Content-Security-Policy' => false ]; $responseHeaders = headers_list(); foreach ($responseHeaders as $header) { $parts = explode(':', $header, 2); if (count($parts) === 2) { $name = trim($parts[0]); if (isset($headers[$name])) { $headers[$name] = true; } } } return $headers; } /** * Session Security prüfen */ private function checkSessionSecurity() { return [ 'httponly' => ini_get('session.cookie_httponly') == '1', 'secure' => ini_get('session.cookie_secure') == '1', 'samesite' => ini_get('session.cookie_samesite') === 'Strict', 'strict_mode' => ini_get('session.use_strict_mode') == '1' ]; } /** * File Permissions prüfen */ private function checkFilePermissions() { $criticalFiles = [ __DIR__ . '/../../../config/database.php' => '0400', __DIR__ . '/../../../.env' => '0400', __DIR__ . '/../../../backups' => '0755' ]; $permissions = []; foreach ($criticalFiles as $file => $expected) { if (file_exists($file)) { $actual = substr(sprintf('%o', fileperms($file)), -4); $permissions[$file] = [ 'expected' => $expected, 'actual' => $actual, 'secure' => $actual <= $expected ]; } } return $permissions; } /** * Database Security prüfen */ private function checkDatabaseSecurity() { try { $stmt = $this->conn->prepare(' SELECT COUNT(*) as user_count FROM ws_user WHERE role = "admin" '); $stmt->execute(); $adminCount = $stmt->fetchAssociative()['user_count']; $stmt = $this->conn->prepare(' SELECT COUNT(*) as failed_logins FROM ws_security_log WHERE event = "login_failed" AND created_at > DATE_SUB(NOW(), INTERVAL 1 HOUR) '); $stmt->execute(); $failedLogins = $stmt->fetchAssociative()['failed_logins']; return [ 'admin_users' => $adminCount, 'recent_failed_logins' => $failedLogins, 'secure' => $adminCount > 0 && $failedLogins < 100 ]; } catch (Exception $e) { return [ 'admin_users' => 0, 'recent_failed_logins' => 0, 'secure' => false, 'error' => $e->getMessage() ]; } } /** * Security Score berechnen */ private function calculateSecurityScore($status) { $score = 0; $total = 0; // SSL Score $total++; if ($status['ssl_enabled']) { $score++; } // Security Headers Score $headerScore = 0; $headerTotal = count($status['security_headers']); foreach ($status['security_headers'] as $enabled) { if ($enabled) $headerScore++; } $score += ($headerScore / $headerTotal); $total++; // Session Security Score $sessionScore = 0; $sessionTotal = count($status['session_security']); foreach ($status['session_security'] as $enabled) { if ($enabled) $sessionScore++; } $score += ($sessionScore / $sessionTotal); $total++; // File Permissions Score $fileScore = 0; $fileTotal = count($status['file_permissions']); foreach ($status['file_permissions'] as $file) { if ($file['secure']) $fileScore++; } $score += ($fileScore / $fileTotal); $total++; // Database Security Score $total++; if ($status['database_security']['secure']) { $score++; } return round(($score / $total) * 100, 1); } /** * Recent Security Events */ private function getRecentSecurityEvents() { try { $stmt = $this->conn->prepare(' SELECT event, level, ip_address, user_agent, created_at FROM ws_security_log ORDER BY created_at DESC LIMIT 10 '); $stmt->execute(); return $stmt->fetchAllAssociative(); } catch (Exception $e) { return []; } } /** * Rate Limit Statistics */ private function getRateLimitStatistics() { try { $stmt = $this->conn->prepare(' SELECT action, COUNT(*) as attempts, COUNT(CASE WHEN created_at > DATE_SUB(NOW(), INTERVAL 1 HOUR) THEN 1 END) as recent_attempts FROM ws_rate_limit WHERE created_at > DATE_SUB(NOW(), INTERVAL 24 HOUR) GROUP BY action '); $stmt->execute(); return $stmt->fetchAllAssociative(); } catch (Exception $e) { return []; } } /** * Security Settings abrufen */ private function getSecuritySettings() { return [ 'csrf_protection' => $this->config->get('CSRF_PROTECTION', true), 'rate_limiting' => $this->config->get('RATE_LIMITING', true), 'session_timeout' => $this->config->get('SESSION_TIMEOUT', 3600), 'max_login_attempts' => $this->config->get('MAX_LOGIN_ATTEMPTS', 5), 'password_min_length' => $this->config->get('PASSWORD_MIN_LENGTH', 8), 'require_strong_password' => $this->config->get('REQUIRE_STRONG_PASSWORD', true), 'two_factor_auth' => $this->config->get('TWO_FACTOR_AUTH', false), 'ssl_required' => $this->config->get('SSL_REQUIRED', true), 'security_headers' => $this->config->get('SECURITY_HEADERS', true), 'file_upload_security' => $this->config->get('FILE_UPLOAD_SECURITY', true) ]; } /** * Security Settings aktualisieren */ private function updateSecuritySettings() { $settings = [ 'CSRF_PROTECTION' => isset($_POST['csrf_protection']), 'RATE_LIMITING' => isset($_POST['rate_limiting']), 'SESSION_TIMEOUT' => intval($_POST['session_timeout'] ?? 3600), 'MAX_LOGIN_ATTEMPTS' => intval($_POST['max_login_attempts'] ?? 5), 'PASSWORD_MIN_LENGTH' => intval($_POST['password_min_length'] ?? 8), 'REQUIRE_STRONG_PASSWORD' => isset($_POST['require_strong_password']), 'TWO_FACTOR_AUTH' => isset($_POST['two_factor_auth']), 'SSL_REQUIRED' => isset($_POST['ssl_required']), 'SECURITY_HEADERS' => isset($_POST['security_headers']), 'FILE_UPLOAD_SECURITY' => isset($_POST['file_upload_security']) ]; foreach ($settings as $key => $value) { $this->config->set($key, $value); } $this->addFlashMessage('Sicherheitseinstellungen wurden aktualisiert', 'success'); $this->redirect('/admin/security/settings'); } /** * Backup erstellen */ private function createBackup() { $description = $_POST['description'] ?? 'Manuelles Backup'; $result = $this->backup->createFullBackup($description); if ($result['success']) { $this->addFlashMessage('Backup erfolgreich erstellt: ' . $result['backup_name'], 'success'); } else { $this->addFlashMessage('Backup fehlgeschlagen: ' . $result['error'], 'error'); } $this->redirect('/admin/security/backup'); } /** * Backup wiederherstellen */ private function restoreBackup() { $backupFile = $_POST['backup_file'] ?? ''; if (!$backupFile || !file_exists($backupFile)) { $this->addFlashMessage('Backup-Datei nicht gefunden', 'error'); $this->redirect('/admin/security/backup'); } $options = [ 'restore_database' => isset($_POST['restore_database']), 'restore_files' => isset($_POST['restore_files']), 'restore_config' => isset($_POST['restore_config']) ]; $result = $this->backup->restoreBackup($backupFile, $options); if ($result['success']) { $this->addFlashMessage('Backup erfolgreich wiederhergestellt', 'success'); } else { $this->addFlashMessage('Backup-Wiederherstellung fehlgeschlagen: ' . $result['error'], 'error'); } $this->redirect('/admin/security/backup'); } /** * Backup herunterladen */ private function downloadBackup() { $backupFile = $_GET['file'] ?? ''; if (!$backupFile || !file_exists($backupFile)) { $this->addFlashMessage('Backup-Datei nicht gefunden', 'error'); $this->redirect('/admin/security/backup'); } header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . basename($backupFile) . '"'); header('Content-Length: ' . filesize($backupFile)); readfile($backupFile); exit; } /** * Backup löschen */ private function deleteBackup() { $backupFile = $_POST['backup_file'] ?? ''; if (!$backupFile || !file_exists($backupFile)) { $this->addFlashMessage('Backup-Datei nicht gefunden', 'error'); $this->redirect('/admin/security/backup'); } if (unlink($backupFile)) { $this->addFlashMessage('Backup erfolgreich gelöscht', 'success'); } else { $this->addFlashMessage('Fehler beim Löschen des Backups', 'error'); } $this->redirect('/admin/security/backup'); } /** * Backup-Liste anzeigen */ private function showBackupList() { $backups = $this->backup->getBackupList(); $backupStatus = $this->backup->checkBackupStatus(); $this->render('admin/security/backup.html.twig', [ 'title' => 'Backup-Verwaltung', 'backups' => $backups, 'backup_status' => $backupStatus ]); } /** * Security Logs abrufen */ private function getSecurityLogs($filters, $limit, $offset) { try { $whereConditions = []; $params = []; if (!empty($filters['level'])) { $whereConditions[] = 'level = ?'; $params[] = $filters['level']; } if (!empty($filters['event'])) { $whereConditions[] = 'event LIKE ?'; $params[] = '%' . $filters['event'] . '%'; } if (!empty($filters['date_from'])) { $whereConditions[] = 'created_at >= ?'; $params[] = $filters['date_from'] . ' 00:00:00'; } if (!empty($filters['date_to'])) { $whereConditions[] = 'created_at <= ?'; $params[] = $filters['date_to'] . ' 23:59:59'; } $whereClause = !empty($whereConditions) ? 'WHERE ' . implode(' AND ', $whereConditions) : ''; $sql = " SELECT id, event, level, ip_address, user_agent, created_at FROM ws_security_log $whereClause ORDER BY created_at DESC LIMIT $limit OFFSET $offset "; $stmt = $this->conn->prepare($sql); $stmt->execute($params); return $stmt->fetchAllAssociative(); } catch (Exception $e) { return []; } } /** * Security Logs Count */ private function getSecurityLogsCount($filters) { try { $whereConditions = []; $params = []; if (!empty($filters['level'])) { $whereConditions[] = 'level = ?'; $params[] = $filters['level']; } if (!empty($filters['event'])) { $whereConditions[] = 'event LIKE ?'; $params[] = '%' . $filters['event'] . '%'; } if (!empty($filters['date_from'])) { $whereConditions[] = 'created_at >= ?'; $params[] = $filters['date_from'] . ' 00:00:00'; } if (!empty($filters['date_to'])) { $whereConditions[] = 'created_at <= ?'; $params[] = $filters['date_to'] . ' 23:59:59'; } $whereClause = !empty($whereConditions) ? 'WHERE ' . implode(' AND ', $whereConditions) : ''; $sql = "SELECT COUNT(*) as total FROM ws_security_log $whereClause"; $stmt = $this->conn->prepare($sql); $stmt->execute($params); return $stmt->fetchAssociative()['total']; } catch (Exception $e) { return 0; } } /** * Rate Limiting Config abrufen */ private function getRateLimitingConfig() { return [ 'login' => ['max_attempts' => 5, 'window' => 300], 'api' => ['max_attempts' => 100, 'window' => 3600], 'register' => ['max_attempts' => 3, 'window' => 1800], 'password_reset' => ['max_attempts' => 3, 'window' => 3600], 'review' => ['max_attempts' => 10, 'window' => 3600], 'contact' => ['max_attempts' => 5, 'window' => 1800] ]; } /** * Rate Limiting aktualisieren */ private function updateRateLimiting() { $actions = ['login', 'api', 'register', 'password_reset', 'review', 'contact']; foreach ($actions as $action) { $maxAttempts = intval($_POST[$action . '_max_attempts'] ?? 5); $window = intval($_POST[$action . '_window'] ?? 300); // In Konfiguration speichern $this->config->set('RATE_LIMIT_' . strtoupper($action) . '_MAX', $maxAttempts); $this->config->set('RATE_LIMIT_' . strtoupper($action) . '_WINDOW', $window); } $this->addFlashMessage('Rate Limiting-Einstellungen aktualisiert', 'success'); $this->redirect('/admin/security/rate_limiting'); } /** * SSL Status abrufen */ private function getSSLStatus() { return [ 'enabled' => isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on', 'certificate' => $this->getSSLCertificateInfo(), 'headers' => $this->checkSecurityHeaders() ]; } /** * SSL Certificate Info */ private function getSSLCertificateInfo() { if (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] !== 'on') { return null; } $host = $_SERVER['HTTP_HOST']; $context = stream_context_create(['ssl' => ['capture_peer_cert' => true]]); $socket = stream_socket_client("ssl://$host:443", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context); if ($socket) { $cert = stream_context_get_params($socket); fclose($socket); if (isset($cert['options']['ssl']['peer_certificate'])) { $certInfo = openssl_x509_parse($cert['options']['ssl']['peer_certificate']); return [ 'subject' => $certInfo['subject']['CN'] ?? '', 'issuer' => $certInfo['issuer']['CN'] ?? '', 'valid_from' => date('Y-m-d H:i:s', $certInfo['validFrom_time_t']), 'valid_to' => date('Y-m-d H:i:s', $certInfo['validTo_time_t']), 'days_remaining' => ceil(($certInfo['validTo_time_t'] - time()) / 86400) ]; } } return null; } /** * SSL Configuration abrufen */ private function getSSLConfiguration() { return [ 'force_https' => $this->config->get('FORCE_HTTPS', true), 'hsts_enabled' => $this->config->get('HSTS_ENABLED', true), 'hsts_max_age' => $this->config->get('HSTS_MAX_AGE', 31536000), 'secure_cookies' => $this->config->get('SECURE_COOKIES', true) ]; } /** * SSL Configuration aktualisieren */ private function updateSSLConfiguration() { $settings = [ 'FORCE_HTTPS' => isset($_POST['force_https']), 'HSTS_ENABLED' => isset($_POST['hsts_enabled']), 'HSTS_MAX_AGE' => intval($_POST['hsts_max_age'] ?? 31536000), 'SECURE_COOKIES' => isset($_POST['secure_cookies']) ]; foreach ($settings as $key => $value) { $this->config->set($key, $value); } $this->addFlashMessage('SSL/TLS-Konfiguration aktualisiert', 'success'); $this->redirect('/admin/security/ssl'); } }