Übersicht

25. Security

Datenbankzugriff

Alle Server-Aktionen laufen über einen Service-Role-Client (prontoAdmin()), der Row Level Security bewusst umgeht — Zugriffskontrolle erfolgt daher ausschließlich im Anwendungscode (userId/kunde_id-Filterung in jeder Abfrage), nicht über RLS-Policies auf Client-Ebene. Der Browser/Client hat nie einen direkten, ungeprüften Datenbankzugriff.

Tenant-Isolation

Jede Aktion — ob eingeloggt oder öffentlich (Buchungsseite, Formular, Newsletter) — ist strikt an eine kunde_id/workspace gebunden. Öffentliche Buchungs-Endpunkte prüfen zusätzlich explizit kalender.kunde_id === eingabe.kundeId, bevor ein Termin angelegt wird, damit eine falsch übergebene Kalender-ID keinen fremden Kalender treffen kann.

Plan-Gating ist immer serverseitig

Jede planpflichtige Funktion (Automatisierungen, Newsletter, KI) wird über requireFeature(userId, feature) serverseitig anhand der authentifizierten userId geprüft — UI-seitiges Ausblenden (usePlan().canAccess(...)) ist ausschließlich UX, niemals der eigentliche Schutzmechanismus (siehe 26-billing.md).

Geheimnisse (Tokens, Codes, Passwörter)

  • Bestätigungscodes und Passwort-Reset-Token werden nie im Klartext gespeichert, sondern als SHA-256-Hash (email_verification_codes.code_hash, password_reset_tokens.token_hash) — beide zeitlich begrenzt (15 Minuten) und einmalig verwendbar.
  • OAuth-Zugangs-/Refresh-Token (Google, Zoom) sowie IMAP/SMTP-Zugangsdaten werden serverseitig gespeichert, nie an den Client ausgeliefert.

Spam-/Missbrauchsschutz

Siehe 24-rate-limits.md: Honeypot, Mindest-Ausfüllzeit, IP-Hash-Rate-Limit auf allen öffentlichen Formular-/Anmelde-Endpunkten. IP-Adressen werden nie im Klartext gespeichert, nur als SHA-256-Hash.

Admin-Zugriff

Admin-Rechte sind eine feste, serverseitig konfigurierte E-Mail-Allowlist (PRONTO_ADMIN_EMAILS) — kein datenbankgespeichertes, client-manipulierbares Flag.

Webhook-Verifizierung

  • Stripe-Webhook: Signaturprüfung (verifyWebhook) gegen das Stripe-Secret.
  • WhatsApp-Webhook: Verifizierung über WHATSAPP_VERIFY_TOKEN beim initialen Meta-Handshake.
  • Interne Cron-Callbacks (Erinnerungen, Follow-ups, System-E-Mails): geschützt durch ein Shared Secret im Header x-pronto-cron-secret, verglichen gegen FOLLOWUP_CRON_SECRET.

DSGVO

  • Formular- und Newsletter-Einsendungen erfordern eine explizite, serverseitig erzwungene Datenschutz-Einwilligung.
  • Double-Opt-in für Newsletter-Anmeldungen.
  • Jede Newsletter-Mail enthält einen funktionierenden Abmeldelink.

Passwort-Reset — unabhängig von Supabase Auth Hooks

Bewusste Design-Entscheidung: Statt eines Supabase-Auth-Hooks (der in der Vergangenheit zu nicht erreichbaren Webhooks führte) nutzt PRONTO ein vollständig eigenständiges, von Supabase unabhängiges Passwort-Reset-System mit eigener Token-Tabelle — siehe 22-authentication.md.