medicus.global · 2026-05-05
"Alle Informationen habe ich selbst eingegeben, und ich bin damit
einverstanden, dass Rettungspersonal und Ärzte sie lesen können —
um ihnen das bestmögliche Vorwissen über meine ganz persönliche
Situation zu geben, besonders im Notfall. Meine Liebe zum Leben
ist größer als die Datenschutzregeln des Landes."
Das ist die erwachsene, freiwillige Entscheidung hinter jedem erstellten My-Medicus-Profil. Unsere Aufgabe ist es, die Wahl der Patientin zu ehren: Daten sicher zu speichern, sie nur dem zu zeigen, der die richtige PIN hat, der Patientin jeden Scan zu zeigen, und der Patientin zu erlauben, die Karte in dem Moment zu widerrufen, in dem sie verloren ist. Der Rest dieses Dokuments erklärt wie.
"Wie ist eure Sicherheit bei My Medicus?"
In einem Notfall ist der größte Wunsch der Patientin, wieder gesund zu werden. Dem dienen wir. Sicherheit ist das Mittel, nicht der Zweck.
Drei Prinzipien stehen über allem:
Wir haben 19 verschiedene Angriffsszenarien gegen My Medicus dokumentiert und jedes einzelne gegen das Produktionssystem ausgeführt. Das Ergebnis steht unten — ohne Marketing-Sprache, ohne "Industriestandard", ohne "Best Practice". Nur was konkret getestet wurde.
19 Angriffsszenarien gegen zwei Konten in der Live-Datenbank — ein Demo-Konto und ein echter neuer Nutzer. Werkzeuge: direkte HTTP-Anfragen, Parallelausführung, SQL-Introspektion der Produktionsdatenbank, Parsing von Antworten.
Ergebnis: 14 Angriffe trafen auf Verteidigungen, die hielten. 5 Schwachstellen wurden gefunden und am selben Tag geschlossen.
| Angriff | Warum der Angriff scheiterte |
|---|---|
| Diebstahl der gesamten Datenbank | Allergien, Medikation, nationale IDs, Pass, EHIC und Versicherungsnummern sind mit AES-256-GCM verschlüsselt. Der Schlüssel liegt in einem separaten Secret-Store, nie in der Datenbank. Stiehlst du die DB-Datei, bekommst du unlesbaren Text |
| Brute-Force auf 6-stellige PIN | Progressive Verzögerungen treten beim 5. Fehlversuch ein: 5 Sek → 30 Sek → 60 Sek zwischen jedem |
| SQL-Injection im Login-Formular | Alle Datenbankabfragen verwenden parametrisierte Queries — fünf verschiedene Injection-Payloads wurden abgewiesen |
| Direkte API-Aufrufe ohne Login | Alle geschützten Endpoints geben HTTP 401 zurück |
| Token-Raten | Tokens sind 32 Hex-Zeichen, abgeleitet via SHA-256. Suchraum: 16^32. 50 zufällige Versuche → 0 Treffer |
| Path-Traversal zu Backups oder Config | /_db_backups/, /.env, /.git, /medicus.db, /wp-config.php geben alle 404 zurück |
| E-Mail-Enumeration über "Passwort vergessen" | Konsistente generische Antwort, unabhängig davon ob die E-Mail existiert — kein Info-Leak |
| XSS in öffentlichen Feldern | Server lehnt malformed Input ab, keine Reflexion von HTML/JS |
| Physischer Geldbörsen-Diebstahl ohne Karten-PIN | PIN-Sperre stoppt alle nicht-korrekten Versuche |
| 10 parallele Brute-Force-Versuche | Rate-Limit gilt pro Token, nicht pro Request |
| Löschen des Audit-Logs via DELETE-Endpoint | Keine DELETE-Endpoints exponiert |
Raten von Admin-Token (admin, 1234, letmein) |
Alle geben 401 zurück |
| Versuch, den Server dazu zu bringen, Secrets via Fehler-Payloads zu leaken | Weder Verschlüsselungsschlüssel noch Stripe-Secrets erscheinen in der Fehlerausgabe |
| Cross-Origin-Missbrauch von einer anderen Website | Browser blockiert wegen Same-Origin-Policy |
| # | Was | Wie schwer | Was getan wurde |
|---|---|---|---|
| F1 | Login antwortete 200 ms langsamer wenn die E-Mail registriert war als wenn nicht. Ein Angreifer konnte feststellen, ob ein Konto existiert | 🔴 Kritisch | Login führt jetzt immer Argon2id-Verifikation aus (auch gegen unbekannte E-Mails, mit einem Dummy-Hash). Konstante Antwortzeit. Generischer Fehlertext |
| F2 | Keine Sicherheits-Header (HSTS, CSP, X-Frame, X-Content-Type, Referrer-Policy, Permissions-Policy) | 🔴 Kritisch | Alle 6 Header werden jetzt auf allen Antworten via Middleware hinzugefügt. CSP sperrt Script/Img/Style/Frame auf gewhitelistete Sources |
| F3 | Eine gestohlene Karte hat QR + PIN direkt aufgedruckt. Der Dieb hat Zugriff bis der Eigentümer widerruft | 🟠 Hoch | (1) Patient sieht jeden Scan in seinem Dashboard ("Mein Protokoll" — IP-Präfix, Land, Zeitpunkt). (2) Eigentümer kann mit einem Klick die Karte widerrufen → alle alten gedruckten Karten sterben sofort + neue PIN wird generiert |
| F4 | /admin gab SPA-HTML an alle zurück, was die Existenz des Admin-Panels offenbarte |
🟡 Niedrig | Gibt jetzt den gleichen 404 zurück wie nicht-existierende Routes für nicht-authentifizierte Nutzer |
| F5 | Passwort-vergessen konnte missbraucht werden, um den Posteingang eines Opfers zu fluten (1.000 Reset-Mails/Stunde) — und gleichzeitig unsere Sender-Reputation zu schädigen | 🟠 Hoch | Wenn der Nutzer einen gültigen Reset-Token hat (max 2 Stunden alt), wird er wiederverwendet anstatt eine neue Mail zu senden. Plus Rate-Limit pro E-Mail von 2/min |
| Wahl | Begründung |
|---|---|
| Keine nationale Authentifizierung in der Sanitäter-Ansicht | Patient liegt bewusstlos auf dem Golfplatz. Der Sanitäter hat keine nationale Authentifizierung, und selbst wenn, haben wir keine 90 Sekunden, um sie einzuloggen. PIN-auf-Karte ist die praktische Antwort |
| Nicht "Zero-Knowledge" | Der Server KANN die Felder entschlüsseln — das ist Bedingung dafür, dass Sanitäter Daten sehen können. Wir nennen nichts "Zero-Knowledge", was es nicht ist |
| Keine automatische Datenimportierung | Jedes Feld wird vom Nutzer gewählt. Kein Auto-Import von anderen Gesundheitsplattformen |
Wenn ein Sanitäter die Karte legitim mit der korrekten PIN scannt, kann er den Bildschirm fotografieren. Wir können das nicht verhindern — das ist der Preis dafür, dass Daten in einem Notfall überhaupt angezeigt werden können. Verteidigung:
Es ist das gleiche Sicherheitsmodell wie ein physisches medizinisches Erkennungs-Armband bei Diabetikern: Daten sind sichtbar für Rettungspersonal, wir akzeptieren, dass auch ein Taschendieb sie sehen kann. Der Unterschied ist, dass unser Modell dem Patienten Kontrolle darüber gibt, wer was wann sieht — nicht nur Sichtbarkeit.
Fragen? Schreib an peter@medicus.global