← App-Check

Beispielbericht. App, Daten und Funde sind erfunden. Ihr echter Bericht liegt in Ihrem Kundenbereich, mit eigenem Login.

Beispielbericht

App-Check-Bericht.
Oktober 2026.

Im Beispiel ohne Funktion.
App
Terminbuchung für Physiotherapie-Praxen
Gebaut mit
Lovable, Supabase, Stripe, Twilio
Umfang
14 Tabellen, 6 Edge Functions, 22 Seiten, 3 Rollen
Geprüfter Stand
Commit a3f9c21 vom 2. Oktober 2026
Prüfzeitraum
5 Werktage
Fazit

Noch nicht bereit für echte Patientendaten. Aber nah dran.

Fünf kritische Funde in drei Bereichen, alle in wenigen Tagen behebbar. Zehn weitere sollten vor dem Launch erledigt sein, fünf haben Zeit bis danach. Die Basis trägt, ein Neubau ist nicht nötig.

  • 5

    Kritisch

  • 10

    Vor Launch beheben

  • 5

    Nach dem Launch

Vorgehen

So wurde geprüft. Und was nicht.

Code-Review
Das gesamte Repository gelesen: Frontend, sechs Edge Functions und alle Migrationen.
Datenbank
Schema, Policies und Funktionen im Live-Projekt mit den Migrationen abgeglichen. Abweichungen gab es keine.
Test-Konten
Je ein Konto pro Rolle (Patient, Therapeut, Praxis-Admin) auf einer Kopie des Projekts. Mit jedem Konto gezielt versucht, fremde Daten zu lesen und zu ändern.
Ausgelieferter Code
Das JavaScript, das im Browser landet, auf Keys, Secrets und interne Adressen durchsucht.
Werkzeuge
npm audit, der Security Advisor von Supabase und eigene Skripte, die jede Policy mit jeder Rolle testen.
Konfiguration
Die Einstellungen von Supabase Auth, Storage, Stripe und Twilio, mit Lesezugriff.
Nicht geprüft
  • Kein Penetrationstest: keine Angriffe von außen auf das Live-System.
  • Keine Last- und Performance-Tests.
  • Keine rechtliche Bewertung.
  • Nicht die Infrastruktur von Lovable, Supabase, Stripe und Twilio selbst.
  • Keine Barrierefreiheit. Die ist Teil der Produktionsreife.
Schweregrade
  • Kritisch

    Daten oder Geld sind jetzt gefährdet. Sofort beheben, auch wenn die App schon live ist.

  • Vor Launch beheben

    Wird zum Risiko, sobald echte Nutzer da sind. Vor dem Launch beheben.

  • Nach dem Launch

    Kein akutes Risiko. Verbessert Wartbarkeit oder Qualität.

Funde

Was gefunden wurde. Und was zu tun ist.

01Login & Rechte

Kritisch
1.1Kritisch

Rolle selbst änderbar

Fund
Die Rolle steht in der Tabelle profiles in der Spalte role. Die Update-Policy erlaubt jedem Nutzer, seine eigene Zeile vollständig zu ändern, also auch die Rolle.
Warum riskant
Ein Patient kann sich mit einer einzigen Anfrage aus dem Browser zum Praxis-Admin machen. Danach sieht und ändert er alle Termine, Patienten und Rechnungen der Praxis.
Wo
supabase/migrations/20260512_profiles.sql, Policy „Users can update own profile“
Empfehlung
  1. Die Spalte role aus der Update-Policy herausnehmen. Nutzer dürfen nur Name, Telefon und Benachrichtigungen ändern.
  2. Rollen nur über eine Edge Function vergeben, die prüft, ob der Aufrufer Praxis-Admin ist.
  3. Einen Test ergänzen, der die eigene Rolle zu ändern versucht und scheitern muss.
1.2Vor Launch beheben

Admins ohne Zwei-Faktor-Anmeldung

Fund
Praxis-Admins und Therapeuten melden sich nur mit E-Mail und Passwort an. Supabase bietet eine zweite Stufe per Authenticator-App, sie ist nicht aktiviert.
Warum riskant
Diese Konten sehen die Daten aller Patienten einer Praxis. Ein erratenes oder geleaktes Passwort reicht, um sie zu übernehmen.
Wo
Supabase Auth, Einstellungen zu Multi-Factor Authentication
Empfehlung
  1. Die Anmeldung per Authenticator-App (TOTP) in Supabase aktivieren.
  2. Für Therapeuten und Praxis-Admins die zweite Stufe verpflichtend machen und in den Policies prüfen (aal2).
  3. Einen Ablauf für verlorene Geräte festlegen, zum Beispiel die Wiederherstellung durch den Praxis-Admin.
1.3Vor Launch beheben

Schwache Passwortregeln

Fund
Passwörter brauchen nur sechs Zeichen. Die Prüfung gegen bekannte, geleakte Passwörter ist ausgeschaltet.
Warum riskant
Kurze und bekannte Passwörter lassen sich in kurzer Zeit durchprobieren.
Wo
Supabase Auth, Einstellungen zu Passwörtern
Empfehlung
  1. Die Mindestlänge auf zehn Zeichen setzen.
  2. Die Prüfung gegen geleakte Passwörter einschalten.
  3. Den Hinweis im Registrierungsformular anpassen.
Was passt
Login über Supabase Auth mit E-Mail-Bestätigung. Der Passwort-Reset ist korrekt eingerichtet, Sessions laufen nach sieben Tagen ab.

02Datenbank-Policies

Kritisch
2.1Kritisch

Termine für alle lesbar

Fund
Row Level Security ist für die Tabelle appointments aktiv. Die Select-Policy prüft aber nur, ob jemand angemeldet ist: using (true) für die Rolle authenticated.
Warum riskant
Jeder registrierte Patient kann alle Termine abrufen, mit Namen, Telefonnummern und den Notizen der Therapeuten. Das sind Gesundheitsdaten. Ein Leck an dieser Stelle ist meldepflichtig.
Wo
Tabelle appointments, Policy „Enable read access for authenticated users“
Empfehlung
  1. Patienten sehen nur Termine mit patient_id = auth.uid().
  2. Therapeuten sehen nur Termine ihrer Praxis, geprüft über die Tabelle practice_members.
  3. Die Notizen in eine eigene Tabelle auslagern, die nur Therapeuten lesen dürfen.
  4. Für jede Rolle einen Test, der fremde Termine abfragt und leer zurückbekommen muss.
2.2Kritisch

Verordnungen öffentlich abrufbar

Fund
Patienten laden ärztliche Verordnungen als PDF hoch. Der Storage-Bucket documents ist öffentlich, die Dateien liegen unter Patienten-ID und Dateiname.
Warum riskant
Wer den Link kennt oder errät, lädt die Datei ohne Anmeldung herunter. Verordnungen enthalten Diagnosen, und Links landen schnell in Mails, Chats und Browser-Verläufen.
Wo
Supabase Storage, Bucket documents
Empfehlung
  1. Den Bucket auf privat stellen.
  2. Den Zugriff über Storage-Policies regeln: der Patient selbst und die Therapeuten seiner Praxis.
  3. Dateien nur über kurzlebige signierte Links ausliefern, zum Beispiel 60 Sekunden.
  4. Prüfen, ob Links schon weitergegeben wurden, und die Dateien bei Bedarf umbenennen.
2.3Vor Launch beheben

Statistik-Funktion ohne Prüfung

Fund
Die Datenbankfunktion get_practice_stats läuft als SECURITY DEFINER, also mit vollen Rechten. Sie nimmt eine Praxis-ID entgegen und prüft nicht, ob der Aufrufer zu dieser Praxis gehört.
Warum riskant
Jeder angemeldete Nutzer kann Umsätze und Terminzahlen jeder Praxis abfragen. Das sind Geschäftsdaten Ihrer Kunden.
Wo
supabase/migrations/20260603_stats.sql, Funktion get_practice_stats
Empfehlung
  1. Am Anfang der Funktion prüfen, ob auth.uid() Admin der angefragten Praxis ist.
  2. Die Funktion für die Rolle anon sperren.
  3. Die beiden anderen SECURITY-DEFINER-Funktionen sind in Ordnung. Neue Funktionen nach demselben Muster absichern.
Was passt
Alle 14 Tabellen haben Row Level Security aktiviert. Die Tabellen invoices und practices sind korrekt auf die eigene Praxis beschränkt.

03API-Keys & Secrets

Kritisch
3.1Kritisch

Stripe-Secret-Key öffentlich

Fund
Der Key steht in der Umgebungsvariable VITE_STRIPE_SECRET_KEY. Alles mit dem Präfix VITE_ landet im ausgelieferten JavaScript.
Warum riskant
Wer die Seite öffnet, kann den Key auslesen. Damit lassen sich Zahlungen erstatten, Kundendaten aus Stripe abrufen und Auszahlungen einsehen.
Wo
src/lib/stripe.ts, Zeile 4
Empfehlung
  1. Den Key in Stripe sofort neu erzeugen. Der alte gilt als bekannt.
  2. Den neuen Key nur als Secret in Supabase hinterlegen und Zahlungen über eine Edge Function abwickeln.
  3. Im Browser nur den Publishable Key verwenden.
  4. Die übrigen VITE_-Variablen durchsehen. Sie sind alle öffentlich.
3.2Kritisch

Stripe-Webhook ohne Signaturprüfung

Fund
Die Edge Function stripe-webhook markiert Termine als bezahlt, sobald eine Nachricht vom Typ checkout.session.completed eingeht. Ob die Nachricht wirklich von Stripe kommt, prüft sie nicht.
Warum riskant
Jeder kann eine solche Nachricht selbst schicken und Termine als bezahlt markieren, ohne zu zahlen.
Wo
supabase/functions/stripe-webhook/index.ts
Empfehlung
  1. Das Webhook-Secret aus Stripe als Secret in Supabase hinterlegen.
  2. Jede Nachricht mit stripe.webhooks.constructEvent prüfen und ungültige mit Status 400 ablehnen.
  3. Den Zahlungsstatus zusätzlich bei Stripe abfragen, bevor ein Termin als bezahlt gilt.
3.3Nach dem Launch

Alter Twilio-Token im Git-Verlauf

Fund
In einem Commit vom März steht ein Twilio-Token im Klartext. Der Token ist inzwischen ungültig.
Warum riskant
Aktuell kein Risiko. Es zeigt aber, dass Secrets schon einmal im Code gelandet sind.
Wo
Git-Verlauf, Commit 4e1b07d, Datei src/lib/sms.ts
Empfehlung
  1. In Twilio bestätigen, dass der Token wirklich widerrufen ist.
  2. Secret Scanning im Repository einschalten, damit neue Fälle sofort auffallen.
Was passt
Der Service-Role-Key von Supabase wird nur in Edge Functions verwendet. Die .env-Dateien sind von Git ausgeschlossen.

04Eingabevalidierung

Vor Launch beheben
4.1Vor Launch beheben

Eingaben nur im Browser geprüft

Fund
Das Buchungsformular prüft Eingaben nur im Browser. Die Edge Function create-booking übernimmt sie ungeprüft. Das Feld „Anliegen“ hat keine Längengrenze, die Telefonnummer kein Format.
Warum riskant
Wer die Anfrage direkt schickt, umgeht die Prüfung. Sehr lange Texte blähen die Datenbank auf, ungültige Nummern lassen SMS-Erinnerungen ins Leere laufen.
Wo
supabase/functions/create-booking/index.ts
Empfehlung
  1. Ein gemeinsames Schema mit zod für Browser und Edge Function.
  2. Das Anliegen auf 1.000 Zeichen begrenzen, Telefonnummern im internationalen Format prüfen.
  3. Ungültige Anfragen mit einer klaren Fehlermeldung ablehnen.
4.2Vor Launch beheben

Uploads ohne Prüfung von Typ und Größe

Fund
Beim Upload der Verordnung sind alle Dateitypen bis 50 MB erlaubt, auch HTML und SVG.
Warum riskant
Über eine hochgeladene HTML-Datei lässt sich eine täuschend echte Seite unter Ihrer Storage-Adresse ausliefern. Große Dateien treiben die Speicherkosten.
Wo
src/pages/booking/UploadStep.tsx, Bucket documents
Empfehlung
  1. Nur PDF, JPG und PNG zulassen, im Browser und in der Bucket-Konfiguration.
  2. Die Größe auf 10 MB begrenzen.
  3. Dateien immer als Download ausliefern, nie direkt im Browser anzeigen.
4.3Nach dem Launch

Fehlermeldungen zeigen Datenbankdetails

Fund
Schlägt eine Buchung fehl, zeigt die App die Fehlermeldung der Datenbank im Original, mit Tabellen- und Spaltennamen.
Warum riskant
Kein direktes Risiko. Angreifer erfahren aber, wie die Datenbank aufgebaut ist, und Nutzer verstehen die Meldungen nicht.
Wo
src/lib/api.ts, Funktion handleError
Empfehlung
  1. Fehler im Browser durch verständliche Meldungen ersetzen.
  2. Die Details nur im Fehler-Tracking speichern.
Was passt
Preise werden auf dem Server berechnet, nicht aus dem Browser übernommen. Texte werden beim Anzeigen korrekt maskiert.

05Abhängigkeiten

Passt
5.1Nach dem Launch

Zwei Pakete veraltet

Fund
date-fns und react-day-picker sind eine Hauptversion hinter dem aktuellen Stand.
Warum riskant
Kein akutes Risiko. Je länger Updates warten, desto größer werden sie.
Wo
package.json
Empfehlung
  1. Beide Pakete bei Gelegenheit aktualisieren und die Kalenderansicht testen.
  2. Automatische Update-Hinweise aktivieren, zum Beispiel mit Dependabot.
5.2Nach dem Launch

Ungenutztes Paket im Bundle

Fund
moment.js ist installiert und wird in einer Datei importiert, aber nicht mehr verwendet.
Warum riskant
Kein Risiko. Es macht die App aber um etwa 70 KB größer und beim ersten Laden langsamer.
Wo
src/components/LegacyCalendar.tsx
Empfehlung
  1. Den Import und das Paket entfernen.
Was passt
npm audit meldet keine bekannten Sicherheitslücken. Die Lockfile liegt im Repository.

06Kosten & Missbrauch

Vor Launch beheben
6.1Vor Launch beheben

SMS-Versand ohne Limit

Fund
Die Edge Function send-reminder verschickt SMS über Twilio. Jeder angemeldete Nutzer kann sie aufrufen, ein Limit gibt es nicht.
Warum riskant
Ein einziges Skript kann Tausende SMS auslösen. Die Rechnung landet bei Ihnen, jede SMS kostet etwa 8 Cent.
Wo
supabase/functions/send-reminder/index.ts
Empfehlung
  1. Die Funktion nur vom Server aus aufrufen, zum Beispiel per Cron, nicht aus dem Browser.
  2. Ein Limit pro Praxis und Tag einbauen.
6.2Vor Launch beheben

Unbegrenzt viele Reservierungen

Fund
Ein Konto kann beliebig viele Termine gleichzeitig reservieren. Unbezahlte Reservierungen bleiben 24 Stunden bestehen.
Warum riskant
Mit wenigen Konten lässt sich der Kalender einer Praxis für einen ganzen Tag blockieren. Echte Patienten finden dann keinen Termin.
Wo
supabase/functions/create-booking/index.ts
Empfehlung
  1. Höchstens drei offene Reservierungen pro Konto.
  2. Unbezahlte Reservierungen nach 30 Minuten freigeben.
  3. Auffällige Konten im Admin-Bereich markieren.
6.3Nach dem Launch

Keine Kostenwarnungen

Fund
Für Supabase und Twilio sind keine Warnungen bei ungewöhnlichen Kosten eingerichtet.
Warum riskant
Missbrauch fällt erst mit der Monatsrechnung auf.
Wo
Abrechnungseinstellungen von Supabase und Twilio
Empfehlung
  1. In Supabase das Ausgabenlimit aktiv lassen und Warnungen per E-Mail einrichten.
  2. In Twilio ein Ausgabenlimit und eine Warnung ab 50 € pro Tag einrichten.
Was passt
Registrierung mit E-Mail-Bestätigung und Captcha. Die Limits von Supabase Auth sind aktiv.

07DSGVO-Grundlagen

Vor Launch beheben
7.1Vor Launch beheben

Datenstandort USA

Fund
Das Supabase-Projekt liegt in der Region us-east-1. Für Supabase, Stripe und Twilio sind keine Auftragsverarbeitungsverträge abgelegt.
Warum riskant
Termine und Notizen sind Gesundheitsdaten, für sie gelten strengere Regeln. Eine EU-Region ist der einfachere Weg.
Wo
Supabase-Projekteinstellungen
Empfehlung
  1. Ein neues Projekt in der Region Frankfurt (eu-central-1) anlegen und die Daten umziehen.
  2. Auftragsverarbeitungsverträge mit Supabase, Stripe und Twilio abschließen und ablegen.
  3. Die Datenschutzerklärung an den neuen Standort anpassen.
7.2Vor Launch beheben

E-Mail-Adressen in Logs

Fund
Die Edge Functions schreiben bei jeder Buchung die E-Mail-Adresse des Patienten in die Logs.
Warum riskant
Personenbezogene Daten in Logs werden oft vergessen, wenn jemand Auskunft oder Löschung verlangt.
Wo
supabase/functions/*/index.ts
Empfehlung
  1. Nur IDs protokollieren, keine E-Mail-Adressen oder Namen.
  2. Die Aufbewahrung der Logs auf sieben Tage begrenzen.
7.3Vor Launch beheben

Löschung unvollständig

Fund
Löscht ein Patient sein Konto, bleiben seine Termine, Notizen und hochgeladenen Verordnungen erhalten.
Warum riskant
Wer die Löschung seiner Daten verlangt, muss sie auch bekommen, soweit keine Aufbewahrungspflicht besteht.
Wo
supabase/functions/delete-account/index.ts
Empfehlung
  1. Festlegen, welche Daten gelöscht und welche wegen Aufbewahrungspflichten nur gesperrt werden.
  2. Termine anonymisieren, Notizen und Dateien löschen oder sperren.
  3. Die Löschung mit einem Test absichern.
Was passt
Eine Datenschutzerklärung ist vorhanden, es gibt keine Tracking-Cookies, und das Konto lässt sich in der App löschen.

Technische Einschätzung, keine Rechtsberatung. Aufbewahrungspflichten klären Sie bitte mit Ihrer Anwältin oder Ihrem Anwalt.

Maßnahmenplan

Alle Funde. In der richtigen Reihenfolge.

Angebot

Produktionsreife. Zum Festpreis.

7.400 €

Festpreis · 3 Wochen

Alle 20 Funde aus diesem Bericht, dazu Tests, CI/CD, Monitoring, Fehler-Tracking und Backups. Kein Neubau: Ihre App bleibt Ihre App.

  • Woche 1

    Kritische Lücken

    Rollen, Termine, Verordnungen, Stripe-Key und Webhook. Danach liegen keine Patientendaten mehr offen, und niemand bucht, ohne zu zahlen.

    2.900 €

  • Woche 2

    Betrieb

    Zwei-Faktor-Anmeldung, Eingaben und Uploads, Limits für SMS und Reservierungen. Dazu Tests, CI/CD, Monitoring, Fehler-Tracking und Backups.

    2.600 €

  • Woche 3

    Datenschutz & Übergabe

    Umzug nach Frankfurt, bereinigte Logs, vollständige Löschung, die übrigen Punkte, Dokumentation und Übergabe.

    1.900 €

  • Jeder Meilenstein zur Hälfte zum Start und zur Hälfte nach der Abnahme. Für die Abnahme haben Sie fünf Werktage.
  • Der Check hat 1.290 € gekostet. Die Hälfte, 645 €, wird mit der ersten Rechnung verrechnet.
  • Sie können den Bericht auch selbst, mit Ihrem Team oder mit Ihren KI-Tools umsetzen. Das Angebot ist keine Pflicht. Danach prüfe ich auf Wunsch nach, zum halben Preis des Checks.
Ihr Bericht

So sieht Ihr Bericht aus.
Nur mit Ihrer App.

Der Bericht liegt nach fünf Werktagen in Ihrem Kundenbereich, dazu eine Besprechung von 30 Minuten. Festpreis ab 990 €, den genauen Preis nenne ich Ihnen vorab.