536 lines
26 KiB
Twig
536 lines
26 KiB
Twig
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title><?= $title ?> - Webshop Admin</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
|
<link href="https://cdn.jsdelivr.net/npm/chart.js@4.0.0/dist/chart.min.css" rel="stylesheet">
|
|
<style>
|
|
.security-score {
|
|
font-size: 3rem;
|
|
font-weight: bold;
|
|
}
|
|
.score-excellent { color: #28a745; }
|
|
.score-good { color: #17a2b8; }
|
|
.score-warning { color: #ffc107; }
|
|
.score-danger { color: #dc3545; }
|
|
|
|
.security-card {
|
|
border-left: 4px solid #dee2e6;
|
|
transition: all 0.3s ease;
|
|
}
|
|
.security-card.secure { border-left-color: #28a745; }
|
|
.security-card.warning { border-left-color: #ffc107; }
|
|
.security-card.danger { border-left-color: #dc3545; }
|
|
|
|
.status-indicator {
|
|
width: 12px;
|
|
height: 12px;
|
|
border-radius: 50%;
|
|
display: inline-block;
|
|
margin-right: 0.5rem;
|
|
}
|
|
.status-secure { background-color: #28a745; }
|
|
.status-warning { background-color: #ffc107; }
|
|
.status-danger { background-color: #dc3545; }
|
|
|
|
.event-log {
|
|
max-height: 400px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.event-item {
|
|
padding: 0.75rem;
|
|
border-bottom: 1px solid #dee2e6;
|
|
transition: background-color 0.2s;
|
|
}
|
|
.event-item:hover {
|
|
background-color: #f8f9fa;
|
|
}
|
|
|
|
.event-level-error { border-left: 4px solid #dc3545; }
|
|
.event-level-warning { border-left: 4px solid #ffc107; }
|
|
.event-level-info { border-left: 4px solid #17a2b8; }
|
|
|
|
.metric-card {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
color: white;
|
|
border-radius: 0.5rem;
|
|
padding: 1.5rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.metric-value {
|
|
font-size: 2rem;
|
|
font-weight: bold;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.metric-label {
|
|
font-size: 0.875rem;
|
|
opacity: 0.9;
|
|
}
|
|
|
|
.chart-container {
|
|
position: relative;
|
|
height: 300px;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.quick-action {
|
|
background: white;
|
|
border: 1px solid #dee2e6;
|
|
border-radius: 0.5rem;
|
|
padding: 1rem;
|
|
text-align: center;
|
|
transition: all 0.3s ease;
|
|
cursor: pointer;
|
|
}
|
|
.quick-action:hover {
|
|
border-color: #667eea;
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
.quick-action i {
|
|
font-size: 2rem;
|
|
color: #667eea;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container-fluid">
|
|
<div class="row">
|
|
<!-- Sidebar -->
|
|
<nav class="col-md-3 col-lg-2 d-md-block bg-light sidebar">
|
|
<div class="position-sticky pt-3">
|
|
<ul class="nav flex-column">
|
|
<li class="nav-item">
|
|
<a class="nav-link active" href="/admin/security/dashboard">
|
|
<i class="fas fa-shield-alt me-2"></i>Dashboard
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="/admin/security/settings">
|
|
<i class="fas fa-cog me-2"></i>Einstellungen
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="/admin/security/backup">
|
|
<i class="fas fa-database me-2"></i>Backup
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="/admin/security/logs">
|
|
<i class="fas fa-list me-2"></i>Logs
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="/admin/security/rate_limiting">
|
|
<i class="fas fa-tachometer-alt me-2"></i>Rate Limiting
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="/admin/security/ssl">
|
|
<i class="fas fa-lock me-2"></i>SSL/TLS
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- Main content -->
|
|
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
|
|
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
|
<h1 class="h2">
|
|
<i class="fas fa-shield-alt me-2"></i>Sicherheits-Dashboard
|
|
</h1>
|
|
<div class="btn-toolbar mb-2 mb-md-0">
|
|
<div class="btn-group me-2">
|
|
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="refreshDashboard()">
|
|
<i class="fas fa-sync-alt me-1"></i>Aktualisieren
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-outline-primary" onclick="createBackup()">
|
|
<i class="fas fa-database me-1"></i>Backup erstellen
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Security Score -->
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-body text-center">
|
|
<h5 class="card-title">Sicherheits-Score</h5>
|
|
<div class="security-score score-<?= $security_status.overall_score >= 90 ? 'excellent' : ($security_status.overall_score >= 70 ? 'good' : ($security_status.overall_score >= 50 ? 'warning' : 'danger')) ?>">
|
|
<?= $security_status.overall_score ?>%
|
|
</div>
|
|
<p class="text-muted">
|
|
<?php if ($security_status.overall_score >= 90): ?>
|
|
<i class="fas fa-check-circle text-success me-1"></i>Ausgezeichnete Sicherheit
|
|
<?php elseif ($security_status.overall_score >= 70): ?>
|
|
<i class="fas fa-check text-info me-1"></i>Gute Sicherheit
|
|
<?php elseif ($security_status.overall_score >= 50): ?>
|
|
<i class="fas fa-exclamation-triangle text-warning me-1"></i>Verbesserungen empfohlen
|
|
<?php else: ?>
|
|
<i class="fas fa-times-circle text-danger me-1"></i>Kritische Sicherheitsprobleme
|
|
<?php endif; ?>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Security Metrics -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-3">
|
|
<div class="metric-card">
|
|
<div class="metric-value">
|
|
<i class="fas fa-shield-alt me-2"></i><?= $backup_status.backup_count ?>
|
|
</div>
|
|
<div class="metric-label">Backups verfügbar</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="metric-card">
|
|
<div class="metric-value">
|
|
<i class="fas fa-clock me-2"></i><?= $backup_status.backup_age_hours ?? 0 ?>
|
|
</div>
|
|
<div class="metric-label">Stunden seit letztem Backup</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="metric-card">
|
|
<div class="metric-value">
|
|
<i class="fas fa-exclamation-triangle me-2"></i><?= count(array_filter($recent_events, fn($e) => $e.level === 'error')) ?>
|
|
</div>
|
|
<div class="metric-label">Kritische Events (24h)</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="metric-card">
|
|
<div class="metric-value">
|
|
<i class="fas fa-ban me-2"></i><?= array_sum(array_column($rate_limit_stats, 'recent_attempts')) ?>
|
|
</div>
|
|
<div class="metric-label">Rate Limit Verstöße (1h)</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Security Status -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-6">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-lock me-2"></i>SSL/TLS Status
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="security-card <?= $security_status.ssl_enabled ? 'secure' : 'danger' ?> p-3 mb-3">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<span class="status-indicator status-<?= $security_status.ssl_enabled ? 'secure' : 'danger' ?>"></span>
|
|
<strong>HTTPS</strong>
|
|
</div>
|
|
<span class="badge bg-<?= $security_status.ssl_enabled ? 'success' : 'danger' ?>">
|
|
<?= $security_status.ssl_enabled ? 'Aktiv' : 'Inaktiv' ?>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<?php foreach ($security_status.security_headers as $header => $enabled): ?>
|
|
<div class="security-card <?= $enabled ? 'secure' : 'warning' ?> p-3 mb-2">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<span class="status-indicator status-<?= $enabled ? 'secure' : 'warning' ?>"></span>
|
|
<strong><?= $header ?></strong>
|
|
</div>
|
|
<span class="badge bg-<?= $enabled ? 'success' : 'warning' ?>">
|
|
<?= $enabled ? 'Aktiv' : 'Inaktiv' ?>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-6">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-database me-2"></i>Datenbank-Sicherheit
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="security-card <?= $database_security.secure ? 'secure' : 'danger' ?> p-3 mb-3">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<span class="status-indicator status-<?= $database_security.secure ? 'secure' : 'danger' ?>"></span>
|
|
<strong>Admin-Benutzer</strong>
|
|
</div>
|
|
<span class="badge bg-<?= $database_security.secure ? 'success' : 'danger' ?>">
|
|
<?= $database_security.admin_users ?> Benutzer
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="security-card <?= $database_security.recent_failed_logins < 50 ? 'secure' : 'warning' ?> p-3 mb-2">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<span class="status-indicator status-<?= $database_security.recent_failed_logins < 50 ? 'secure' : 'warning' ?>"></span>
|
|
<strong>Fehlgeschlagene Logins (1h)</strong>
|
|
</div>
|
|
<span class="badge bg-<?= $database_security.recent_failed_logins < 50 ? 'success' : 'warning' ?>">
|
|
<?= $database_security.recent_failed_logins ?>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recent Events & Quick Actions -->
|
|
<div class="row">
|
|
<div class="col-md-8">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-list me-2"></i>Letzte Sicherheits-Events
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="event-log">
|
|
<?php if (empty($recent_events)): ?>
|
|
<div class="text-center text-muted py-4">
|
|
<i class="fas fa-check-circle fa-3x mb-3"></i>
|
|
<p>Keine kritischen Events in den letzten 24 Stunden</p>
|
|
</div>
|
|
<?php else: ?>
|
|
<?php foreach ($recent_events as $event): ?>
|
|
<div class="event-item event-level-<?= $event.level ?>">
|
|
<div class="d-flex justify-content-between align-items-start">
|
|
<div>
|
|
<strong><?= htmlspecialchars($event.event) ?></strong>
|
|
<br>
|
|
<small class="text-muted">
|
|
<i class="fas fa-globe me-1"></i><?= htmlspecialchars($event.ip_address) ?>
|
|
<span class="mx-2">•</span>
|
|
<i class="fas fa-clock me-1"></i><?= date('d.m.Y H:i', strtotime($event.created_at)) ?>
|
|
</small>
|
|
</div>
|
|
<span class="badge bg-<?= $event.level === 'error' ? 'danger' : ($event.level === 'warning' ? 'warning' : 'info') ?>">
|
|
<?= strtoupper($event.level) ?>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-bolt me-2"></i>Schnellaktionen
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-6 mb-3">
|
|
<div class="quick-action" onclick="createBackup()">
|
|
<i class="fas fa-database"></i>
|
|
<div><strong>Backup</strong></div>
|
|
<small class="text-muted">Erstellen</small>
|
|
</div>
|
|
</div>
|
|
<div class="col-6 mb-3">
|
|
<div class="quick-action" onclick="clearLogs()">
|
|
<i class="fas fa-trash"></i>
|
|
<div><strong>Logs</strong></div>
|
|
<small class="text-muted">Bereinigen</small>
|
|
</div>
|
|
</div>
|
|
<div class="col-6 mb-3">
|
|
<div class="quick-action" onclick="testSSL()">
|
|
<i class="fas fa-lock"></i>
|
|
<div><strong>SSL</strong></div>
|
|
<small class="text-muted">Testen</small>
|
|
</div>
|
|
</div>
|
|
<div class="col-6 mb-3">
|
|
<div class="quick-action" onclick="securityScan()">
|
|
<i class="fas fa-search"></i>
|
|
<div><strong>Scan</strong></div>
|
|
<small class="text-muted">Durchführen</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Backup Status -->
|
|
<div class="card mt-3">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-database me-2"></i>Backup Status
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
<span>Letztes Backup:</span>
|
|
<span class="text-muted">
|
|
<?= $backup_status.last_backup ? date('d.m.Y H:i', strtotime($backup_status.last_backup)) : 'Nie' ?>
|
|
</span>
|
|
</div>
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
<span>Backup-Alter:</span>
|
|
<span class="text-<?= ($backup_status.backup_age_hours ?? 999) > 24 ? 'danger' : 'success' ?>">
|
|
<?= $backup_status.backup_age_hours ?? 0 ?> Stunden
|
|
</span>
|
|
</div>
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<span>Backup benötigt:</span>
|
|
<span class="badge bg-<?= $backup_status.backup_needed ? 'danger' : 'success' ?>">
|
|
<?= $backup_status.backup_needed ? 'Ja' : 'Nein' ?>
|
|
</span>
|
|
</div>
|
|
|
|
<?php if ($backup_status.backup_needed): ?>
|
|
<div class="mt-3">
|
|
<button class="btn btn-warning btn-sm w-100" onclick="createBackup()">
|
|
<i class="fas fa-exclamation-triangle me-1"></i>Backup erstellen
|
|
</button>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.0.0/dist/chart.min.js"></script>
|
|
<script>
|
|
// Dashboard Funktionen
|
|
function refreshDashboard() {
|
|
location.reload();
|
|
}
|
|
|
|
function createBackup() {
|
|
if (confirm('Möchten Sie ein neues Backup erstellen?')) {
|
|
fetch('/admin/security/backup?action=create', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-Token': getCSRFToken()
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
alert('Backup erfolgreich erstellt!');
|
|
location.reload();
|
|
} else {
|
|
alert('Fehler beim Erstellen des Backups: ' + data.error);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
alert('Fehler beim Erstellen des Backups');
|
|
});
|
|
}
|
|
}
|
|
|
|
function clearLogs() {
|
|
if (confirm('Möchten Sie alle Sicherheits-Logs löschen?')) {
|
|
fetch('/admin/security/logs/clear', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-Token': getCSRFToken()
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
alert('Logs erfolgreich bereinigt!');
|
|
location.reload();
|
|
} else {
|
|
alert('Fehler beim Bereinigen der Logs');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function testSSL() {
|
|
fetch('/admin/security/ssl/test', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-Token': getCSRFToken()
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
alert('SSL-Test erfolgreich: ' + data.message);
|
|
} else {
|
|
alert('SSL-Test fehlgeschlagen: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function securityScan() {
|
|
if (confirm('Möchten Sie einen vollständigen Sicherheits-Scan durchführen?')) {
|
|
fetch('/admin/security/scan', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-Token': getCSRFToken()
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
alert('Sicherheits-Scan abgeschlossen!\n\nGefundene Probleme: ' + data.issues.length);
|
|
location.reload();
|
|
} else {
|
|
alert('Sicherheits-Scan fehlgeschlagen: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function getCSRFToken() {
|
|
return document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';
|
|
}
|
|
|
|
// Auto-Refresh alle 5 Minuten
|
|
setInterval(() => {
|
|
// Nur wenn Dashboard aktiv ist
|
|
if (!document.hidden) {
|
|
fetch('/admin/security/dashboard/status')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
// Update nur wenn sich etwas geändert hat
|
|
if (data.needs_refresh) {
|
|
location.reload();
|
|
}
|
|
});
|
|
}
|
|
}, 300000); // 5 Minuten
|
|
</script>
|
|
</body>
|
|
</html> |