fbeta ePA3-Service-OpenSource
HTTP-Header-Injection in den inneren VAU-Requests
fbetas ePA3-Service baut innere VAU-HTTP-Requests durch f-String-Interpolation vom Aufrufer kontrollierter Werte (KVNR insurantId, Umgebungsvariable USER_AGENT, Argument boundary_string) direkt in rohe Header-Zeilen, womit die CRLF-Validierung der requests-Bibliothek umgangen wird. Ein authentisierter DiGA-Nutzer kann durch eine KVNR mit eingebettetem CRLF einen zweiten x-insurantid-Header in den verschlüsselten VAU-Kanal einschleusen.
Beschreibung
Alle vier Methoden, die innere VAU-HTTP-Requests bauen, interpolieren vom Aufrufer kontrollierte Parameter direkt per f-String in rohe HTTP-Header und kodieren den zusammengesetzten Request als UTF-8-Bytestring für die AES-GCM-Verschlüsselung. Die requests-Bibliothek, die CRLF in Header-Werten ablehnt, wird vollständig umgangen; an keiner Stelle der inneren Request-Konstruktion findet CRLF-Bereinigung statt.
VAUProtokoll.py:507-515 (Muster wiederholt sich in allen 4 Methoden)
inner_request = "GET /epa/authz/v1/getNonce HTTP/1.1\r\n"inner_request += f"Host: {self.host}\r\n"inner_request += "Accept: application/json\r\n"inner_request += "Content-Type: application/json\r\n"inner_request += f"x-useragent: {USER_AGENT}\r\n"inner_request += f"x-insurantid: {insurant_id}\r\n"inner_request += "\r\n"inner_request = inner_request.encode("utf-8")Einschleusbare Parameter:
Einschleusbare Parameter
insurant_id : metadata['insurantId'], nur als JSON-Schema-String validiertUSER_AGENT : os.getenv('USER_AGENT'); vergiftet jeden Request der Sessionboundary_string : vom Aufrufer in upload_document() geliefert; Content-Type-Breakoutself.host : Host-String des Servers in jedem RequestDie vier betroffenen Methoden sind get_nonce() (507-515), send_authorization_request_sc() (542-552), send_authcode_sc() (634-644) und upload_document() (688-698). Der Wert von insurantId stammt aus dem metadata['insurantId']-Feld der DiGA, das ausschließlich als "type": "string" validiert wird, ohne Muster-Vorgabe; das KVNR-Format ist strikt [A-Z][0-9]{9}, doch das JSON-Schema setzt es nicht durch.
Während eines Testlaufs wurde eine KVNR mit eingebettetem CRLF (X123456789\r\nX-Injected: ...) über die DiGA-Schnittstelle geliefert. Der Relay entschlüsselte den inneren HTTP-Verkehr und beobachtete, wie beide Header in die innere Verarbeitung des ePA-Servers gelangten:
Entschlüsselter innerer HTTP-Verkehr nach CRLF-Injection
Decrypted inner HTTP (REQUEST): GET /epa/authz/v1/getNonce HTTP/1.1 Host: epa-as-2.dev.epa4all.de Accept: application/json Content-Type: application/json x-useragent: usrAgent/1.1.0 x-insurantid: X000000000 X-Injected: CRLF-header-injection-confirmed
CRLF in insurantId passed through to inner HTTP.f-string interpolation, no sanitization.Auswirkung
- Cross-Patient-Datenoffenlegung: Das Einschleusen eines zweiten
x-insurantid-Headers mit einer anderen KVNR kann je nach Header-Vorrangs-Verhalten des ePA-Servers (first-wins vs. last-wins) dazu führen, dass Datensätze einer anderen Versicherten zurückgegeben werden. - Autorisierungs-Bypass: Ein eingeschleuster
Authorization: Bearer <token>-Header könnte den normalen Authentifizierungsfluss im inneren HTTP-Handler des ePA-Servers umgehen. - Sitzungsweites Poisoning: Eine bösartige
USER_AGENT-Umgebungsvariable schleust für die gesamte Sitzungslaufzeit Header in jeden inneren HTTP-Request ein. - Request-Smuggling: In
send_authcode_sc()wird derContent-Length-Header auslen(body_json)berechnet, bevor eine Injection greifen kann; wenn eingeschleuste Daten die Header-Body-Grenze verschieben, entsteht eine HTTP-Desync-Angriffsfläche. - Wenn auch der gematik: VAU-Handshake führt nur 2 von 6 vorgeschriebenen Server-Schlüssel-Prüfungen aus oder fbeta: VAU-Server-Authentifizierungs-Bypass durch zirkuläres Zertifikatsvertrauen unmitigiert ist, kontrolliert ein MITM die innere VAU-Klartext-Ebene ohne DiGA-Nutzer-Privileg; der eingeschleuste
x-insurantidkommt per Relay statt durch einen authentisierten Request, und PR sinkt auf N.
Abhilfe
Aktualisieren Sie fbeta ePA3-Service-OpenSource auf 1.3.0 oder höher. Der Fix bereinigt jeden vom Aufrufer kontrollierten Wert vor der Interpolation (jede Zeichenkette mit \r oder \n wird abgelehnt) und validiert insurantId an der Bibliotheksgrenze gegen das KVNR-Format ^[A-Z]\d{9}$. Die längerfristige Behebung im PR ersetzt die manuelle String-Zusammensetzung innerer HTTP-Requests durch eine Bibliothek, die Header-Validierung, korrekte Content-Length-Berechnung und passende Kodierung mitbringt.
Checkliste für Betreiber
Auf fbeta ePA3-Service-OpenSource 1.3.0 oder höher aktualisieren.
Alle Versionen vor 1.3.0 sind betroffen. Der Fix liegt in Pull Request #11.
KVNR an der DiGA-Grenze validieren, nicht erst in der Bibliothek.
Das KVNR-Format
^[A-Z]\d{9}$ist strikt definiert. Wenden Sie die Regex am frühesten Punkt an, an dem der Wert Ihre Vertrauensgrenze überschreitet (Formulareingabe, API-Request-Body), damit eine ungültige KVNR scheitert, bevor sie diese Bibliothek erreicht.Umgebungsvariablen prüfen.
Die
USER_AGENT-Umgebungsvariable war ein Poisoning-Vektor: Ein Wert mit CRLF vergiftete jeden Request der Sitzungslaufzeit. Wenn Ihr Deployment weitere Umgebungsvariablen in innere HTTP-Header übernimmt, validieren Sie sie beim Start, nicht zum Zeitpunkt der Verwendung.Kein HTTP per Hand zusammenbauen.
Wo Sie den Code selbst kontrollieren, ersetzen Sie manuelle
inner_request += f"..."-Muster durch eine Bibliothek, die eine wohlgeformte HTTP-Nachricht produziert. Der Fehler hier wäre nicht möglich gewesen, wenn Header-Werte zwischen Aufrufer und Verschlüsselungsschritt eine Validierungsschicht durchlaufen hätten.
Bewertung im Detail
x-insurantid (oder einen anderen eingeschleusten Header) so honoriert, dass Datensätze zurückgegeben oder modifiziert werden; nicht in jeder Server-Konfiguration garantiert. AC:H trägt die Unsicherheit des serverseitigen Schritts.PR:LEin authentisierter DiGA-Nutzer wird benötigt, um die bösartige KVNR über die DiGA-Schnittstelle einzubringen.UI:NKeine Interaktion eines zweiten Beteiligten erforderlich.S:UDie Auswirkung ist auf Datensätze beschränkt, die über den inneren HTTP-Handler des ePA-Servers erreichbar sind.C:HWorst-Case-Cross-Patient-Datenoffenlegung, wenn ein duplizierter x-insurantid dazu führt, dass Datensätze einer anderen Betroffenen zurückgegeben werden. Die serverseitige Header-Vorrangs-Logik ist der unsichere Schritt, getragen von AC:H; fbeta selbst bewertet den gleichen Vektor in ihrem GHSA.I:HWorst-Case-Modifikation der Semantik des inneren HTTP-Requests durch Autorisierungs-Bypass und Request-Smuggling. Das abhängige serverseitige Verhalten ist der unsichere Schritt, getragen von AC:H.A:NKeine Auswirkung auf die Verfügbarkeit.Referenzen
So können wir helfen
Wer wir sind
Die Sicherheitsforscher hinter diesem Sicherheitshinweis.

Dr. rer. nat. Simon Weber
Senior Pentester & MedSec-Forscher
Ich evaluiere Ihr SaMD mit derselben branchenprägenden Sicherheitsexpertise, die ich dem BAK MV für die Überarbeitung des B3S-Standards beigetragen habe.
- Promotion über Krankenhaus-Cybersicherheit
- Kritische Schwachstellen in Krankenhaussystemen gefunden
- Alumni der THB MedSec-Forschungsgruppe
- gematik Security Hero

Dipl.-Inf. Volker Schönefeld
Senior Application Security Expert
Als ehemaliger CTO und Entwickler, der zum Pentester wurde, arbeite ich mit Ihrem Team zusammen, um Schwachstellen aufzudecken und Lösungen zu finden, die zu Ihrer Architektur passen.
- 20+ Jahre als CTO, 50+ Mio. App-Downloads
- Architektur und Absicherung großer IoT-Flotten
- Certified Web Exploitation Specialist
- gematik Security Hero
Penetrationstest gesucht?
Machine Spirits ist spezialisiert auf Sicherheitsbewertungen für Medizinprodukte und Gesundheits-IT. Von MDR-Penetrationstests bis C5-Cloud-Compliance helfen wir MedTech-Unternehmen, regulatorische Anforderungen zu erfüllen.
