Sicherheitsstatus

Dieser Commit ist enthalten in:
2025-06-07 21:01:45 +02:00
Ursprung 25b8a9a33d
Commit df5e0e0365
7 geänderte Dateien mit 771 neuen und 11 gelöschten Zeilen

Datei anzeigen

@@ -0,0 +1,115 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Gesperrte IPs - Admin Panel</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<nav class="navbar navbar-dark bg-dark">
<div class="container">
<span class="navbar-brand">🎛️ Lizenzverwaltung</span>
<div class="d-flex align-items-center">
<span class="text-white me-3">Angemeldet als: {{ username }}</span>
<a href="/logout" class="btn btn-outline-light btn-sm">Abmelden</a>
</div>
</div>
</nav>
<div class="container py-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1>🔒 Gesperrte IPs</h1>
<div>
<a href="/" class="btn btn-secondary">← Dashboard</a>
<a href="/audit" class="btn btn-secondary">📋 Audit-Log</a>
</div>
</div>
<div class="card">
<div class="card-header">
<h5 class="mb-0">IP-Sperrverwaltung</h5>
</div>
<div class="card-body">
{% if blocked_ips %}
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>IP-Adresse</th>
<th>Versuche</th>
<th>Erster Versuch</th>
<th>Letzter Versuch</th>
<th>Gesperrt bis</th>
<th>Letzter User</th>
<th>Letzte Meldung</th>
<th>Status</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
{% for ip in blocked_ips %}
<tr class="{% if ip.is_active %}table-danger{% else %}table-secondary{% endif %}">
<td><code>{{ ip.ip_address }}</code></td>
<td><span class="badge bg-danger">{{ ip.attempt_count }}</span></td>
<td>{{ ip.first_attempt }}</td>
<td>{{ ip.last_attempt }}</td>
<td>{{ ip.blocked_until }}</td>
<td>{{ ip.last_username or '-' }}</td>
<td><strong>{{ ip.last_error or '-' }}</strong></td>
<td>
{% if ip.is_active %}
<span class="badge bg-danger">GESPERRT</span>
{% else %}
<span class="badge bg-secondary">ABGELAUFEN</span>
{% endif %}
</td>
<td>
<div class="btn-group btn-group-sm" role="group">
{% if ip.is_active %}
<form method="post" action="/security/unblock-ip" class="d-inline">
<input type="hidden" name="ip_address" value="{{ ip.ip_address }}">
<button type="submit" class="btn btn-success"
onclick="return confirm('IP {{ ip.ip_address }} wirklich entsperren?')">
🔓 Entsperren
</button>
</form>
{% endif %}
<form method="post" action="/security/clear-attempts" class="d-inline ms-1">
<input type="hidden" name="ip_address" value="{{ ip.ip_address }}">
<button type="submit" class="btn btn-warning"
onclick="return confirm('Alle Versuche für IP {{ ip.ip_address }} zurücksetzen?')">
🗑️ Reset
</button>
</form>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="alert alert-info">
<strong>Keine gesperrten IPs vorhanden.</strong>
Das System läuft ohne Sicherheitsvorfälle.
</div>
{% endif %}
</div>
</div>
<div class="card mt-4">
<div class="card-body">
<h5 class="card-title"> Informationen</h5>
<ul class="mb-0">
<li>IPs werden nach <strong>{{ 5 }} fehlgeschlagenen Login-Versuchen</strong> für <strong>24 Stunden</strong> gesperrt.</li>
<li>Nach <strong>2 Versuchen</strong> wird ein CAPTCHA angezeigt.</li>
<li>Bei <strong>5 Versuchen</strong> wird eine E-Mail-Benachrichtigung gesendet (wenn aktiviert).</li>
<li>Gesperrte IPs können manuell entsperrt werden.</li>
<li>Die Fehlermeldungen werden zufällig ausgewählt für zusätzliche Verwirrung.</li>
</ul>
</div>
</div>
</div>
</body>
</html>

Datei anzeigen

@@ -118,10 +118,10 @@
</div>
</div>
<!-- Backup-Status -->
<!-- Backup-Status und Sicherheit nebeneinander -->
<div class="row g-3 mb-4">
<div class="col-12">
<div class="card">
<div class="col-md-6">
<div class="card h-100">
<div class="card-body">
<h5 class="card-title">💾 Backup-Status</h5>
{% if stats.last_backup %}
@@ -152,7 +152,76 @@
</div>
</div>
</div>
<!-- Sicherheitsstatus -->
<div class="col-md-6">
<div class="card h-100">
<div class="card-body">
<h5 class="card-title">🔒 Sicherheitsstatus</h5>
<div class="d-flex justify-content-between align-items-center mb-3">
<span>Sicherheitslevel:</span>
<span class="badge bg-{{ stats.security_level }} fs-6">{{ stats.security_level_text }}</span>
</div>
<div class="row text-center">
<div class="col-6">
<h4 class="text-danger mb-0">{{ stats.blocked_ips_count }}</h4>
<small class="text-muted">Gesperrte IPs</small>
</div>
<div class="col-6">
<h4 class="text-warning mb-0">{{ stats.failed_attempts_today }}</h4>
<small class="text-muted">Fehlversuche heute</small>
</div>
</div>
<a href="/security/blocked-ips" class="btn btn-sm btn-outline-danger mt-3">IP-Verwaltung →</a>
</div>
</div>
</div>
</div>
<!-- Sicherheitsereignisse -->
{% if stats.recent_security_events %}
<div class="row g-3 mb-4">
<div class="col-12">
<div class="card">
<div class="card-header bg-dark text-white">
<h6 class="mb-0">🚨 Letzte Sicherheitsereignisse</h6>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-sm mb-0">
<thead>
<tr>
<th>Zeit</th>
<th>IP-Adresse</th>
<th>Versuche</th>
<th>Fehlermeldung</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{% for event in stats.recent_security_events %}
<tr>
<td>{{ event.last_attempt }}</td>
<td><code>{{ event.ip_address }}</code></td>
<td><span class="badge bg-secondary">{{ event.attempt_count }}</span></td>
<td><strong class="text-danger">{{ event.error_message }}</strong></td>
<td>
{% if event.blocked_until %}
<span class="badge bg-danger">Gesperrt bis {{ event.blocked_until }}</span>
{% else %}
<span class="badge bg-warning">Aktiv</span>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
{% endif %}
<div class="row g-3">
<!-- Bald ablaufende Lizenzen -->

Datei anzeigen

@@ -5,6 +5,70 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Login - Lizenzverwaltung</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.error-failed {
background-color: #dc3545 !important;
color: white !important;
font-weight: bold !important;
font-size: 1.5rem !important;
text-align: center !important;
padding: 1rem !important;
border-radius: 0.5rem !important;
text-transform: uppercase !important;
animation: shake 0.5s;
box-shadow: 0 0 20px rgba(220, 53, 69, 0.5);
}
.error-blocked {
background-color: #6f42c1 !important;
color: white !important;
font-weight: bold !important;
font-size: 1.2rem !important;
text-align: center !important;
padding: 1rem !important;
border-radius: 0.5rem !important;
animation: pulse 2s infinite;
}
.error-captcha {
background-color: #fd7e14 !important;
color: white !important;
font-weight: bold !important;
font-size: 1.2rem !important;
text-align: center !important;
padding: 1rem !important;
border-radius: 0.5rem !important;
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-10px); }
20%, 40%, 60%, 80% { transform: translateX(10px); }
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.7; }
100% { opacity: 1; }
}
.attempts-warning {
background-color: #ffc107;
color: #000;
padding: 0.5rem;
border-radius: 0.25rem;
text-align: center;
margin-bottom: 1rem;
font-weight: bold;
}
.security-info {
font-size: 0.875rem;
color: #6c757d;
text-align: center;
margin-top: 1rem;
}
</style>
</head>
<body class="bg-light">
<div class="container">
@@ -15,11 +79,17 @@
<h2 class="text-center mb-4">🔐 Admin Login</h2>
{% if error %}
<div class="alert alert-danger" role="alert">
<div class="error-{{ error_type|default('failed') }}">
{{ error }}
</div>
{% endif %}
{% if attempts_left is defined and attempts_left > 0 and attempts_left < 5 %}
<div class="attempts-warning">
⚠️ Noch {{ attempts_left }} Versuch(e) bis zur IP-Sperre!
</div>
{% endif %}
<form method="post">
<div class="mb-3">
<label for="username" class="form-label">Benutzername</label>
@@ -29,12 +99,27 @@
<label for="password" class="form-label">Passwort</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
{% if show_captcha %}
<div class="mb-3">
<div class="g-recaptcha" data-sitekey="{{ recaptcha_site_key|default('6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI') }}"></div>
</div>
{% endif %}
<button type="submit" class="btn btn-primary w-100">Anmelden</button>
</form>
<div class="security-info">
🛡️ Geschützt durch Rate-Limiting und IP-Sperre
</div>
</div>
</div>
</div>
</div>
</div>
{% if show_captcha %}
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
{% endif %}
</body>
</html>