Core – Sicherheitsmodell
Auf dieser Seite
English (summary)
Security is layered: transport (HTTPS redirect strategy, HSTS), response headers and a nonce-based CSP with Trusted Types (CMS\Security), hardened sessions (HttpOnly, Secure, SameSite=Strict, strict mode, ID regeneration, role-based lifetimes, device binding), authentication with rate limiting, MFA (TOTP, backup codes), passkeys and LDAP (CMS\Auth, CMS\Auth\AuthManager), per-action one-time CSRF tokens (1 h TTL, 20 per action), a global CSRF guard for public POST requests, an application firewall (SecurityRuntimeService), anti-spam, prepared statements, HTML sanitising (HTMLPurifier, Editor.js sanitizer), upload validation, encrypted secrets (SettingsService, AES-256-CBC) and audit logging.
Deutsch
1. Transport
Konstante (config/app.php) |
Standard | Wirkung |
|---|---|---|
CMS_HTTPS_REDIRECT_STRATEGY |
upstream |
wer HTTP→HTTPS umleitet: vorgelagerter Proxy/Webserver (upstream), Apache-.htaccess oder Core-PHP; disabled möglich |
CMS_HSTS_MODE |
https-only |
HSTS nur bei HTTPS-Anfragen |
CMS_HSTS_MAX_AGE |
31536000 | Gültigkeit in Sekunden |
HTTPS wird auch hinter Proxys erkannt (X-Forwarded-Proto, X-Forwarded-SSL, Front-End-Https, Port 443).
2. Sicherheitsheader (Security::setSecurityHeaders())
X-Content-Type-Options: nosniff, X-Frame-Options (Standard DENY), X-XSS-Protection: 0, Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy: geolocation=(), microphone=(), camera=(), Cross-Origin-Opener-Policy: same-origin, Cross-Origin-Resource-Policy: same-site, Strict-Transport-Security (bei HTTPS). Admin und API zusätzlich Cache-Control: no-store, Pragma: no-cache, X-Robots-Tag: noindex, nofollow.
3. Content-Security-Policy
Pro Request wird ein Nonce erzeugt (Security::getNonce(), im Template Security::nonceAttr()).
default-src 'self';
script-src 'self' 'nonce-…' [+ freigegebene Quellen];
style-src 'self' 'nonce-…' https://fonts.googleapis.com;
img-src 'self' data: blob: […]; font-src 'self' data: https://fonts.gstatic.com;
connect-src 'self' […]; media-src 'self' data: blob:;
object-src 'none'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; manifest-src 'self';
upgrade-insecure-requests;
trusted-types cms365 default sanitize-html dompurify; require-trusted-types-for 'script'
- Mit
CMS_DEBUG = truewird die Richtlinie nur alsContent-Security-Policy-Report-Onlygesendet. - Zusätzliche HTTPS-Hosts für
script-src,connect-src,img-src(z. B. Matomo) gibt Code überSecurity::allowCspSources()frei – typischerweise im Hookcms_csp_prepare. - Inline-Styles in vollständigen HTML-Antworten schreibt
Http\InlineStyleRewriterin nonce-geschützte Klassen um (style-src-attr). - Konsequenz für Entwickler: keine Inline-Skripte, keine
on*=-Attribute, keineval; JavaScript als Datei, DOM-Zuweisungen über Trusted-Types-Policies.
4. Sessions
session.cookie_httponly = 1,cookie_securebei HTTPS,cookie_samesite = Strict,use_only_cookies,use_strict_mode; neue Session-ID beim ersten Start und beim Login.- Laufzeiten je Rolle: Admin 8 h (
perf_session_timeout_admin), Mitglied 30 Tage (perf_session_timeout_member); abgelaufene Sessions werden beim Laden verworfen. - Session-Bindung an ein HMAC-signiertes Geräte-Cookie (
cms_device, 2 h, enthält Benutzer-ID, Zeitstempel und Hash der Session-ID) erschwert Session-Diebstahl. - Cookie-Domain wird bei Alias-/Hub-Domains bewusst host-only gesetzt (
CMS/index.php).
5. Authentifizierung
| Baustein | Umsetzung |
|---|---|
| Passwort-Hash | password_hash(…, PASSWORD_BCRYPT, cost 12) |
| Passwortrichtlinie | ≥ 12 Zeichen, Groß-/Kleinbuchstabe, Ziffer, Sonderzeichen (Auth::getPasswordPolicyDefinition()) |
| Login-Sperre | MAX_LOGIN_ATTEMPTS (5) in LOGIN_TIMEOUT (300 s) je IP über Security::checkDbRateLimit() |
| Generische Fehlermeldung | „Ungültige Anmeldedaten.“ – kein Hinweis, ob Benutzer existiert |
| Kontostatus | nur active darf sich anmelden |
| MFA | TOTP (Totp, TotpAdapter) + Backup-Codes; Login liefert MFA_REQUIRED → /mfa-challenge |
| Passkeys | WebAuthn (WebAuthnAdapter, cms_passkey_credentials) |
| LDAP | LdapAuthProvider (LdapRecord) |
| Passwort-Reset | Token in cms_password_resets, einmalig, Ablauf 60 min, Rate-Limits, konstante Antwortzeit |
| Abmeldung | nur mit CSRF-Token logout |
| Audit | loginSuccess, loginFailed, Rollenwechsel, Sperren |
Rollen- und Rechtemodell: ../admin/users-groups/RBAC.md.
6. CSRF
Security::generateToken($aktion)erzeugt 64 Hex-Zeichen; je Aktion werden bis zu 20 Tokens für 1 Stunde in der Session gehalten (mehrere Tabs möglich).verifyToken()prüft zeitkonstant (hash_equals) und entwertet das Token (Einmalverwendung).verifyPersistentToken()prüft ohne Entwertung (Editor-Uploadseditorjs_media, KI-Endpunkte, Favoriten).- Jede Admin-Seite hat eine eigene Aktion (z. B.
admin_pages); öffentliche Formulare nutzenform_guard(globale Prüfung im Router), Kommentarecomment_<postId>, Checkoutcheckout_process.
7. Anfrageschutz
- Firewall mit IP-/CIDR-/User-Agent-/Länderregeln, Simulationsmodus und Rate-Limit (../admin/security/FIREWALL.md).
- API-Rate-Limit 60 Anfragen/60 s je IP (
CMS\Api), Web-Vitals mit eigenem Limit und Same-Origin-Prüfung. - Anti-Spam für Formulare (../admin/security/ANTISPAM.md).
- Sichere Weiterleitungen:
Router::redirect()und Login-Redirects lassen nur interne/erlaubte Ziele zu. - Ausgehende Requests (
Http\Client): Host-Allowlists für Updates/Marketplace/KI, HTTPS-Pflicht für Cloud-Ziele, keine privaten Netze außer explizit freigegebenen (Ollama).
8. Daten
- Datenbank nur über PDO mit nativen Prepared Statements (
ATTR_EMULATE_PREPARES = false). - Rich-Text über
PurifierService(HTMLPurifier), Editor.js überEditorJsSanitizer/EditorJsHtmlSanitizer, Frontend zusätzlich DOMPurify. - Ausgabe-Escaping über
htmlspecialcharsbzw. Helfer inincludes/functions/escaping.php. - Uploads: Endungs-Allowlist, gefährliche Endungen gesperrt, MIME- und Bildinhaltsprüfung, Dateinamen-Bereinigung,
.htaccessim Upload-Ordner (../admin/media/MEDIA.md). - Geheimnisse in der Datenbank:
SettingsServiceverschlüsselt mit AES-256-CBC (Präfixenc:). Der Schlüssel wird ausAUTH_KEYundSECURE_AUTH_KEYabgeleitet – werden diese Konstanten geändert, sind gespeicherte Secrets (SMTP, Azure, Graph, KI-Provider) nicht mehr lesbar und müssen neu eingegeben werden. - ZIP-Verarbeitung (Updates, Marketplace, Backups): Grenzen für Einträge/Größe, Pfadprüfung, SHA-256-Pflicht.
9. Protokollierung
AuditLogger (Tabelle cms_audit_log), Logger (Dateien je Kanal), cms_security_log (Firewall/Anti-Spam), SecurityAlertService (E-Mail-Alarme). Diagnoseausgaben werden bereinigt (Bearer-Tokens, Passwörter → ***).
10. Prüfen
Sicherheits-Audit unter /admin/security-audit (../admin/security/SECURITY-AUDIT.md), Checkliste ../admin/PRUEF-CHECKLISTE.md.
Verwandte Dokumente
ARCHITECTURE.md · ../admin/security/README.md · ../member/MEMBER-SECURITY.md