| Finanz-PIN (Zugriffsschranke Geldbereiche) | Chef, Büro, System | Zweite PIN-Abfrage vor den fünf Geldbereichen; Freischaltung gilt pro Benutzername, verfällt nach 30 Minuten ohne Nutzung und verlängert sich bei jedem Zugriff. Wer ein Benutzerkonto mit aktiver Zwei-Faktor-Absicherung hat, gibt statt der PIN den Code der Authenticator-App ein. Drei Schalter davor - global an/aus, Ausnahmen pro Bereich, Ausnahmen pro Benutzer - sind in den Einstellungen konfigurierbar. | Rechnungen & Zahlungen | Bewusst aus | lib/finance-pin.ts |
| Master-PIN Wiederherstellung (Vault-Support-Weg) | Chef, System | Master-PIN aus der Server-Einrichtung, zeitgleich verglichen; fünf Fehlversuche schalten den PIN-Weg auf einen einmaligen Bestätigungslink per E-Mail um (24 Stunden gültig, nur einmal verwendbar). Eine erfolgreiche Freischaltung gilt 15 Minuten und verlängert sich bei jedem Zugriff. Nur die Rolle Inhaber kommt überhaupt bis zu dieser Abfrage. | – | Live | lib/vault.ts |
| Admin-Login & Sitzungen | Büro, Chef, System | Zwei Wege nebeneinander. Der neue führt eine Sitzung in der Datenbank (widerrufbar, an ein Benutzerkonto und seine Rolle gebunden, mit letztem Zugriff und optionaler Zwei-Faktor-Bestätigung). Der alte prüft ein signiertes Cookie aus den Zugangsdaten der Server-Einrichtung und gilt immer als Inhaber. Beide liegen im selben Sitzungsausweis im Browser; das System erkennt am Präfix, welcher gilt. | Büro-CRM (Admin), Aufträge & Werkstatt-Ablauf (API) | Live | lib/admin-auth.ts |
| Content-Security-Policy (Browser-Härtung) | System, Kunde | middleware.ts setzt Header serverseitig; next.config.ts-Kommentar warnt explizit, die Policy dort NICHT zu duplizieren | Öffentliche Website, Büro-CRM (Admin) | Live | middleware.ts |
| Herkunfts-Prüfung (CSRF-Schutz) | System | requireSameOrigin() prueft Origin/Referer gegen PUBLIC_ALLOWED_HOSTS bei nicht-GET/HEAD/OPTIONS; faellt bei leerem PUBLIC_ALLOWED_HOSTS offen (nur geloggt, nicht blockiert) — als Konfigurationsluecke im Code selbst dokumentiert | Aufträge & Werkstatt-Ablauf (API) | Live | lib/api-guard.ts |
| IP-Sperrliste | Chef, Büro | Zaehler je Identifier (normalisierte IP oder 'tel:'-Nummer) in bot_ip_blocks; 7 Preisabfragen in 24 h ⇒ Sperre fuer 7 Tage; Entsperren nur ueber die Admin-Route mit dem Recht 'Benutzerverwaltung' | – | Live | app/api/admin/ip-blocks |
| Kunden-Link-Token (Angebot/Termin/Bericht) | Kunde | Zwei Bauarten - (1) zustandsbehaftete Einmal-Token in customer_tokens: 256 Bit Zufall, in der Datenbank nur als SHA-256-Hash, gebunden an Zweck (kind) und Ressource (refId), Standard 30 Tage, widerrufbar; (2) zustandsloses HMAC-SHA256-Token fuer Termin-Selbstbedienung (lib/booking-token.ts), zeitkonstant geprueft, ohne Datenbankeintrag | Kundenportal, Kundenlinks: Angebot, Termin, Bericht (API) | Live | lib/db/customer-tokens.ts |
| Mechaniker-PIN & Geräte-Bindung | Werkstatt, Büro | Geraetebindung per persoenlichem QR-Token (mechanics.qrToken, unbefristet, per rotateMechanicQr neu ausstellbar) oder per Auftrags-QR (jobSessions.token, 24 h); resolveBoundMechanic() prueft Token und aktiv; authorizeTablet() bindet jede Tablet-Anfrage an den Auftrag bzw. den eigenen Buehnen-Teil und sperrt gevaultete Auftraege; rlHit() begrenzt je IP und je Token | Mechaniker-Tablet, Mechaniker-Tablet & Anwesenheit (API) | Teilweise | lib/db/tablet-session.ts |
| Rate-Limiting (Missbrauchsbremse) | System | clientIpFrom() ermittelt die Client-Adresse spoof-sicher, danach rateLimit() (gleitendes Fenster, Arbeitsspeicher) bzw. rateLimitDb() (fixes Fenster, SQLite) je nach RATE_LIMIT_STORE; rlHit() ist der fertige 429-Wrapper fuer die Tablet- und Mechaniker-Wege | Öffentliche System-Endpunkte (unauthentifiziert) | Live | lib/rate-limit.ts |
| Rollen & Rechte (Berechtigungskatalog) | Chef, Büro | requirePermission(session, recht) prueft die in verifyAuth berechneten effektiven Rechte (Rollen-Standard aus ROLE_PERMISSIONS plus per-User-Override aus users.permissions) und antwortet sonst mit 403; owner erhaelt immer ALL_PERMISSIONS, ein Override wird dort ignoriert; Alt-Sitzungen aus dem gemeinsamen Login gelten als owner | – | Live | lib/admin-auth.ts |
| Verschlüsselter Secret-Store | Chef, System | Werte werden mit AES-256-GCM verschluesselt in einer eigenen Tabelle abgelegt; der 32-Byte-Schluessel wird per HKDF-SHA256 aus einem Grundgeheimnis abgeleitet und liegt nie in der Datenbank; die Oberflaeche bekommt nur ein configured-Kennzeichen oder einen maskierten Hinweis zurueck, nie den Klartext | SMS-Gateway | Live | MEM:sms-gateway-feature.md |
| Datei-Upload-Absicherung | System, Büro | Magic-Byte-Pruefung der ersten 4-16 Bytes gegen eine kurze Allowlist (JPEG, PNG, GIF, WEBP, HEIC, PDF); Dateiname wird serverseitig neu vergeben als Zufallsname mit erkannter Endung; alle Schreibwege loesen ueber resolveDataPath() unterhalb von DATA_DIR auf und lehnen Traversal, absolute Pfade, Laufwerksbuchstaben, Nullbytes und prozentkodierte Segmente ab; der ZIP-Export pinnt zusaetzlich auf den Ordner genau dieses Auftrags | Dokumente, Prüfungen & Fotos | Teilweise | lib/path-safe.ts |