28800, // 8 h — ein Upload-Nachmittag ohne erneutes Anmelden // Ohne Slash am Ende — Absicht: ein Cookie mit `path=/dateien/` wird bei einem // Aufruf von `/dateien` (ohne Slash) NICHT mitgesendet (RFC 6265 §5.1.4). Dann // fehlt das CSRF-Token in der Session und jeder Login scheitert als „ungültig". // Apache leitet `/dateien` zwar auf `/dateien/` um, der Dev-Server aber nicht. // `/dateien` deckt beide Formen ab und greift nicht auf `/datenschutz` über. 'path' => '/dateien', 'secure' => $https, // lokal (http) sonst kein Cookie und damit kein Login 'httponly' => true, 'samesite' => 'Lax', // trägt zusätzlich das fehlende CSRF-Handling der App (index.php:301) ]); ini_set('session.gc_maxlifetime', '28800'); } /* * Brute-Force-Bremse. Files Gallery bringt keine mit — im gesamten index.php gibt es * kein Rate-Limit, Anmeldeversuche sind unbegrenzt. Zähler und Log sind dieselben wie * bei den Formularen (storage/ratelimit/, storage/logs/spam.log). * Der Login-POST kommt aus der App per fetch() als JSON-Body (index.php:384), aus dem * Login-Formular als normales POST — daher beide Wege prüfen. */ $isLoginAttempt = ($_SERVER['REQUEST_METHOD'] ?? '') === 'POST' && (($_GET['action'] ?? '') === 'login' || isset($_POST['fusername'])); if ($isLoginAttempt && !rate_limit_ok('dateien-login:' . client_ip(), 10, 600)) { log_spam('dateien', 'login-ratelimit'); http_response_code(429); header('Retry-After: 600'); header('Content-Type: text/plain; charset=utf-8'); exit("Zu viele Anmeldeversuche. Bitte in 10 Minuten noch einmal probieren.\n"); } /* * SVG beim Upload auf ausführbare Bestandteile prüfen — die einzige erlaubte Endung, die * Code tragen kann. Die CSP `sandbox` in public/dateien/.htaccess entschärft das im * Browser, ist aber die zweite Reihe: hier landet die Datei gar nicht erst in der Ablage. * Ein Vereins- oder Sponsorenlogo braucht kein