Quelltext des aktuellen Standes
<?php
declare(strict_types=1);
/**
* Sicherheitskonfiguration
*
* Zentrale Sicherheitseinstellungen für das gesamte System.
* SICHERHEITSKRITISCH: Diese Werte direkt auf die Sicherheit des Systems aus.
*/
return [
// Session-Konfiguration
'session' => [
'name' => 'ITDOCS_SESSION',
'lifetime' => (int) ($_ENV['SESSION_LIFETIME'] ?? 28800), // 8 Stunden
'path' => '/',
'domain' => '', // Automatisch aus Host
'secure' => filter_var($_ENV['SESSION_SECURE'] ?? true, FILTER_VALIDATE_BOOLEAN),
'httponly' => true,
// WICHTIG: Lax statt Strict für OAuth-Callbacks
// Strict verhindert, dass Session-Cookies bei Redirects von externen Seiten gesendet werden
'samesite' => 'Lax',
'regenerate_interval' => 1800, // Session-ID alle 30 Minuten erneuern
// Eigenes Session-Verzeichnis: verhindert, dass der System-Cron (Debian/Ubuntu)
// Session-Dateien löscht, bevor die konfigurierte Lifetime abgelaufen ist.
'save_path' => dirname(__DIR__) . '/storage/sessions',
],
// CSRF-Schutz
'csrf' => [
'enabled' => true,
'token_name' => '_csrf',
'header_name' => 'X-CSRF-Token',
'lifetime' => (int) ($_ENV['CSRF_TOKEN_LIFETIME'] ?? 28800), // 8 Stunden (gleich wie Session-Lifetime)
'per_form' => true, // Separate Tokens pro Formular
],
// Rate Limiting
'rate_limit' => [
'enabled' => true,
'requests' => (int) ($_ENV['RATE_LIMIT_REQUESTS'] ?? 1000),
'window' => (int) ($_ENV['RATE_LIMIT_WINDOW'] ?? 60), // Sekunden
'by' => 'ip', // oder 'user'
// Spezielle Limits für sensible Endpunkte
'endpoints' => [
'login' => ['requests' => 15, 'window' => 60],
// LDAP-Login (AD-Bind) muss dieselbe Drossel wie der lokale Login haben,
// sonst wären dort ~1000 Passwortversuche/Minute möglich.
'auth/ldap' => ['requests' => 15, 'window' => 60],
'mfa' => ['requests' => 5, 'window' => 300],
'auth/mfa' => ['requests' => 5, 'window' => 300],
// Muster werden gegen den Pfad geprüft (Präfix oder enthalten) – sie
// müssen daher echten Pfadbestandteilen entsprechen, nicht Sprechnamen.
'password/' => ['requests' => 10, 'window' => 300],
'upload/image' => ['requests' => 50, 'window' => 60],
'files/upload' => ['requests' => 50, 'window' => 60],
'export' => ['requests' => 50, 'window' => 60],
],
],
// Content Security Policy
'csp' => [
'enabled' => true,
'directives' => [
'default-src' => "'self'",
'script-src' => "'self' 'unsafe-inline'",
'style-src' => "'self' 'unsafe-inline'",
'img-src' => "'self' data: blob: https:",
'font-src' => "'self'",
'connect-src' => "'self'",
'frame-ancestors' => "'self'",
'form-action' => "'self'",
'base-uri' => "'self'",
],
],
// Zusätzliche HTTP-Sicherheitsheader
'headers' => [
'X-Content-Type-Options' => 'nosniff',
'X-Frame-Options' => 'SAMEORIGIN',
// Hinweis: X-XSS-Protection wurde bewusst entfernt – der Header ist in
// modernen Browsern deaktiviert/abgekündigt und kann in Altbrowsern sogar
// Schaden anrichten. Maßgeblich ist die Content-Security-Policy.
'Referrer-Policy' => 'strict-origin-when-cross-origin',
'Permissions-Policy' => 'geolocation=(), microphone=(), camera=()',
],
// HSTS (nur bei HTTPS aktivieren)
'hsts' => [
'enabled' => true,
'max_age' => 31536000, // 1 Jahr
'include_subdomains' => true,
'preload' => false,
],
// Passwort-Hashing
'password' => [
'algorithm' => PASSWORD_ARGON2ID,
'options' => [
'memory_cost' => 65536,
'time_cost' => 4,
'threads' => 3,
],
],
// Passwort-Richtlinie (optional, im Backend pflegbar; Default AUS).
// Greift beim Anlegen/Setzen lokaler Passwörter. Ist 'enabled' = false,
// gelten nur die bestehenden Basis-Mindestlängen der jeweiligen Maske.
'password_policy' => [
'enabled' => false,
'min_length' => 12,
'require_uppercase' => false,
'require_lowercase' => false,
'require_number' => false,
'require_special' => false,
],
// Account-Lockout nach zu vielen Fehl-Logins (optional; Default AUS).
// Zählt fehlgeschlagene lokale Logins je Benutzerkonto innerhalb des
// Zeitfensters und sperrt das Konto danach temporär.
'lockout' => [
'enabled' => false,
'max_attempts' => 5, // Fehlversuche bis zur Sperre
'window_minutes' => 15, // Zeitfenster, in dem Fehlversuche zählen
'lockout_minutes' => 15, // Dauer der temporären Sperre
],
// SIEM-Weiterleitung der Audit-Events (optional; Default AUS).
// Ein Hintergrund-Task schickt neue Audit-Einträge an ein externes SIEM /
// zentrales Logsystem – per HTTP-Webhook und/oder UDP-Syslog. 'min_risk'
// begrenzt auf Ereignisse ab dieser Risikostufe (low|medium|high|critical).
'siem' => [
'enabled' => false,
'webhook_url' => '',
'syslog_host' => '',
'syslog_port' => 514,
'min_risk' => 'low',
],
// Periodische Integritätsprüfung der Audit-Hash-Kette (optional; Default AUS).
// Schlägt die Prüfung fehl, werden Administratoren in-App benachrichtigt und
// ein kritisches Audit-Ereignis erzeugt (das ggf. ans SIEM weitergeleitet wird).
'integrity_check' => [
// Standardmäßig AN: Die nächtliche Prüfung der Audit-Hash-Kette meldet
// Manipulationen früh. Sie ist günstig (nur die letzten `sample` Einträge)
// und alarmiert nur bei erkannten Integritätsfehlern. Abschaltbar unter
// /admin/settings/security. Bestehende Instanzen mit gespeicherter
// Einstellung behalten ihren Wert (Runtime-Override greift vor Default).
'enabled' => true,
'sample' => 1000, // Anzahl der zuletzt geprüften Einträge
],
// MFA-Erzwingung (optional; Default AUS). Betrifft nur LOKALE Konten –
// SSO/LDAP-MFA ist Sache des Identity-Providers. Ist 'enforce' aktiv, werden
// betroffene Nutzer ohne eingerichtete MFA-Methode (TOTP/WebAuthn) nach dem
// Login zur MFA-Einrichtung geleitet, bis sie eine Methode hinterlegt haben.
// scope: 'admins' = nur globale Administratoren, 'all' = alle lokalen Konten.
'mfa' => [
'enforce' => false,
'scope' => 'admins',
],
// Verschlüsselung
'encryption' => [
'algorithm' => 'aes-256-gcm',
'key' => $_ENV['STORAGE_ENCRYPTION_KEY'] ?? '',
],
// File Upload Sicherheit
'upload' => [
'max_size' => (int) ($_ENV['MAX_UPLOAD_SIZE'] ?? 52428800), // 50 MB
'allowed_mimes' => [
// Dokumente
'application/pdf',
'application/msword',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.ms-powerpoint',
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'text/plain',
'text/csv',
// Bilder
'image/jpeg',
'image/png',
'image/gif',
'image/webp',
'image/svg+xml',
// Archive
'application/zip',
'application/x-7z-compressed',
],
'denied_extensions' => [
'php', 'phtml', 'php3', 'php4', 'php5', 'php7', 'phps',
'exe', 'bat', 'cmd', 'sh', 'bash',
'js', 'vbs', 'wsf', 'wsh',
'msi', 'dll', 'com', 'scr',
'htaccess', 'htpasswd',
],
],
// IP-Whitelist für Admin-Bereich (optional)
'admin_ip_whitelist' => [
'enabled' => false,
'ips' => [],
],
// Audit-Einstellungen
'audit' => [
'retention_years' => (int) ($_ENV['AUDIT_RETENTION_YEARS'] ?? 10),
// Optionaler, im Backend aktivierbarer Retention-Purge (Default AUS):
// löscht Audit-Einträge älter als 'retention_years'. Die Hash-Kette der
// behaltenen Einträge bleibt über einen gespeicherten Anker prüfbar.
'purge_enabled' => false,
'log_request_body' => false, // Datenschutz
'log_response_body' => false,
'sensitive_fields' => ['password', 'token', 'secret', 'key'],
],
];