Alle Sicherheitshinweise

OHIF Viewer

OIDC-Token-Diebstahl durch extern kontrollierte URL

Eine in der offiziellen OHIF-Viewer-Distribution ausgelieferte Konfiguration ermöglicht es, dass ein präparierter Link die Session-Credentials eines authentifizierten Nutzers an einen angreiferkontrollierten Server sendet. Ein einziger Klick eines angemeldeten Klinik-Nutzers genügt; der Angreifer benötigt keine eigenen Credentials. Die OHIF-Maintainer haben einen Fix in v3.12.2 veröffentlicht: Die betroffenen Datenquellen prüfen die abgerufene URL nun gegen eine vom Betreiber konfigurierte Origin-Allowlist, bevor eine Anfrage den Token des Nutzers übernimmt. Deployments mit v3.12.0 oder älter sollten auf v3.12.2 oder neuer aktualisieren. Die Schwachstelle ist als CVE-2026-12473 erfasst und wurde von CISA als Advisory ICSMA-26-176-02 veröffentlicht.

Verfasst vonVolker Schönefeld, Simon WeberErstveröffentlichung 2026-05-18Vollständige Offenlegung 2026-07-24
SchweregradHochCVSS 8.2CVSS-3.1-VektorAV:N/AC:L/PR:N/UI:R/S:C/C:H/I:L/A:NCWECWE-918 (Server-Side Request Forgery (SSRF))ProduktOHIF ViewerBetroffene VersionenAlle Releases vor v3.12.2, einschließlich der Stable-Linie v3.12.0 (Februar 2026).Behoben inv3.12.2CVECVE-2026-12473

Beschreibung

Zwei Datenquellen, die in der mit der offiziellen OHIF-Distribution ausgelieferten Konfiguration registriert sind, lesen eine URL aus dem ?url=-Query-Parameter und rufen sie ab: ohne Origin-Prüfung, ohne Einschränkung des URL-Schemas, ohne Allowlist. Beide liegen auf automatisch erzeugten Routen (/:modeRouteName/dicomwebproxy und /:modeRouteName/dicomjson), sodass ein präparierter Link sie ohne jede deployment-spezifische Konfiguration erreicht.

DicomWebProxyDataSource/index.ts:17-34 (v3.12.0)

initialize: async ({ params, query }) => {
const url = query.get('url');
if (!url) {
throw new Error(`No url for '${name}'`);
} else {
const response = await fetch(url);
const data = await response.json();
if (!data.servers?.dicomWeb?.[0]) {
throw new Error('Invalid configuration returned by url');
}
dicomWebDelegate = createDicomWebApi(
data.servers.dicomWeb[0].configuration || data.servers.dicomWeb[0],
servicesManager
);
dicomWebDelegate.initialize({ params, query });
}
},

View source →

Das abgerufene Dokument wird als DICOMweb-Serverkonfiguration geparst, und seine Werte für wadoRoot und qidoRoot werden zu den Endpunkten für jede folgende Metadaten- und Bildanfrage. Die zweite Datenquelle nimmt denselben Parameter entgegen und parst die Antwort als Studien-Manifest, in dem jede instance.url zum Ziel eines Bildabrufs wird.

DicomJSONDataSource/index.js:64-80 (v3.12.0)

initialize: async ({ query, url }) => {
if (!url) {
url = query.get('url');
}
let metaData = getMetaDataByURL(url);
// ...
const response = await fetch(url);
const data = await response.json();

View source →

Der Abruf allein ist noch nicht die Offenlegung. Das Credential folgt, weil userAuthenticationService ein globales Singleton ist und nichts seinen Token an eine Origin oder an die anfragende Datenquelle bindet. Sobald der Klinik-Nutzer angemeldet ist, hängen zwei voneinander unabhängige Pfade diesen Token an genau den Endpunkt, den die aktive Datenquelle benennt.

DicomWebDataSource/index.ts:145-152 (v3.12.0)

getAuthorizationHeader = () => {
const xhrRequestHeaders: HeadersInterface = {};
const authHeaders = userAuthenticationService.getAuthorizationHeader();
if (authHeaders && authHeaders.Authorization) {
xhrRequestHeaders.Authorization = authHeaders.Authorization;
}
return xhrRequestHeaders;
};

View source →

Diese Closure wird vor jeder Abfrage neu ausgewertet statt einmalig gespeichert (qidoDicomWebClient.headers = getAuthorizationHeader()), der Header trägt also einen gültigen und keinen veralteten Token. Parallel dazu hängt der Cornerstone-Image-Loader denselben Header über einen globalen beforeSend-Hook an jede Pixeldaten-Anfrage.

initWADOImageLoader.js:28-46 (v3.12.0)

beforeSend: function (xhr) {
const sourceConfig = extensionManager.getActiveDataSource()?.[0].getConfig() ?? {};
const headers = userAuthenticationService.getAuthorizationHeader();
const xhrRequestHeaders = {
Accept: acceptHeader,
};
if (headers) {
Object.assign(xhrRequestHeaders, headers);
}
return xhrRequestHeaders;
},

View source →

Wir haben beide Routen am 19.02.2026 gegen das offizielle Image ohif/app:v3.12.0 mit Keycloak 24.0.5 als OIDC-Provider vollständig bestätigt. Der Link trug selbst keinen Token; der Angreifer-Server erhielt den gültigen Keycloak-Access-Token des angemeldeten Klinik-Nutzers, einmal über den DICOMweb-Abfragepfad und einmal über den Image-Loader-Pfad.

Ausgabe des Angreifer-Servers (Bearer-Token gekürzt)

[*] GET /config.json - serving DICOMweb configuration
[!] GET /steal/studies?limit=101&offset=0&StudyInstanceUID=1.2.3
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
[!] GET /steal/studies/1.2.3/series?includefield=00080021,00080031
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
[*] GET /studies.json - serving study manifest
[!] GET /steal/image.dcm
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...

Der dekodierte Token war der gültige Access-Token des Test-Kontos, ausgestellt vom Keycloak-Realm für den Client ohif-viewer, mit einer Session-Kennung passend zur aktiven Browser-Sitzung. Es handelt sich um ein Credential, nicht um eine Kopie der Studiendaten: gegen das Imaging-Backend wiedereingespielt gewährt es genau die Zugriffe, die dem Nutzer zustehen.

OHIF hat am 21.04.2026 #5963 und am 27.04.2026 #5973 gemergt und damit einen secureConfigFetch-Helper eingeführt. In einem authentifizierten Deployment muss eine Cross-Origin-Konfigurations-URL nun in einer vom Betreiber konfigurierten Allowlist stehen, bevor sie überhaupt abgerufen wird.

secureConfigFetch.js:82-89 (v3.12.2)

if (isAuthenticated && !isSameOrigin) {
const normalizedAllowedOrigins = normalizeAllowedOrigins(allowedOrigins);
if (!normalizedAllowedOrigins.length || !normalizedAllowedOrigins.includes(parsedUrl.origin)) {
throw new Error(
`Blocked remote configuration origin "${parsedUrl.origin}" in authenticated environment`
);
}
}

View source →

Maintainer Joe Boccanfuso bat uns, diesen Fix zu validieren. Am 28.04.2026 haben wir die Testumgebung der Erstmeldung gegen ohif/app:v3.13.0-beta.63 neu aufgebaut und bestätigt, dass der Cross-Origin-Angriff abgewiesen wird, bevor eine Anfrage überhaupt abgesetzt wird. Die Prüfung stützte sich allerdings auf die URL in ihrer geschriebenen Form, nicht auf die tatsächlich zurückgelieferte Antwort. Eine Same-Origin-URL übersprang die Allowlist vollständig und wurde anschließend mit dem Browser-Standard redirect: 'follow' abgerufen, sodass ein einzelner Open Redirect irgendwo auf der Origin des Deployments den ursprünglichen Angriff wiederherstellte. Wir haben einen solchen als nginx-Location return 302 $arg_to in die Testumgebung eingebaut und den Token des Klinik-Nutzers erneut auf zwei getrennten Abfragen abgegriffen. Redirect-Endpunkte dieser Art sind in der Schicht vor einem klinischen Viewer alltäglich: Gateways nach Art von oauth2-proxy, auth_request-Flows, SSO-Portale und OIDC-Callback-Handler.

Wir haben die Lücke am selben Tag gemeldet. Joe eröffnete am Folgetag #5985 gegen master und #5978 als Backport für 3.12 und fasste die Verzweigung in fetchConfigJson so zusammen, dass ein einziger Satz gehärteter Optionen für jeden Abruf gilt.

secureConfigFetch.js:99-107 (v3.12.2)

async function fetchConfigJson(normalizedPolicy) {
const { normalizedUrl } = normalizedPolicy;
const response = await fetch(normalizedUrl, {
method: 'GET',
mode: 'cors',
credentials: 'same-origin',
redirect: 'error',
referrerPolicy: 'no-referrer',
});

View source →

redirect: 'error' bricht den Abruf bei jedem 30x ab und schließt damit den Redirect-Pfad; credentials: 'same-origin' und referrerPolicy: 'no-referrer' begrenzen unabhängig davon, was ein Konfigurationsabruf nach außen mitführen kann. Wir haben am 29.04.2026 beide Pull-Request-Stände lokal gebaut und die Testumgebung gegen jeden laufen lassen: Cross-Origin und Same-Origin-Redirect wurden auf beiden Branches blockiert, ohne Regression. Der Backport-Commit 838f5195 ist ein Vorfahr des Tags v3.12.2, das veröffentlichte Release trägt also nicht nur die Allowlist, sondern auch die Redirect-Härtung.

Auswirkung

  • Ein Angreifer, der einem authentifizierten OHIF-Nutzer einen präparierten Link zustellt, kann bewirken, dass dessen OIDC-Bearer-Token an einen angreiferkontrollierten Server übertragen wird. Der abgegriffene Token lässt sich gegen das Imaging-Backend wiedereinspielen, um auf die Patientenstudien (PHI) zuzugreifen, für die der Nutzer berechtigt ist. Die Ausnutzung erfordert lediglich einen einzigen Klick eines bereits authentifizierten Nutzers und keinerlei Credentials aufseiten des Angreifers.
  • Der Link ist von einer legitimen URL zum Teilen einer Studie nicht zu unterscheiden: gleicher Host, gleiche Pfadstruktur, lediglich das Segment der Datenquelle weicht ab. Die betroffenen Routen existieren im offiziellen Distributions-Image ohne Zutun des Betreibers, ein Deployment ist also standardmäßig exponiert und nicht erst durch eine Fehlkonfiguration.

Abhilfe

Aktualisieren Sie auf OHIF Viewer v3.12.2 oder neuer, das die abgerufene URL gegen eine vom Betreiber konfigurierte Origin-Allowlist prüft und Redirects beim Konfigurationsabruf abweist. Betreiber, die die betroffenen Datenquellen in authentifizierten Deployments benötigen, müssen diese Allowlist zusätzlich konfigurieren; Deployments, die sie nicht nutzen, sollten die nicht genutzten Datenquellen aus ihrer Konfiguration entfernen. Deployments mit v3.12.0 oder älter bleiben bis zum Update betroffen.

Checkliste für Betreiber

  • Auf v3.12.2 oder neuer aktualisieren.

    Alles bis einschließlich v3.12.0 ist betroffen. Prüfen Sie, dass das eingesetzte Image kein älterer 3.13-Vorabstand ist: Die Härtung verteilt sich auf #5963, #5973 und #5985, und erst der letzte davon schließt den Redirect-Pfad.

  • Nicht genutzte Datenquellen entfernen.

    Wenn dicomwebproxy und dicomjson nicht zu Ihrem Workflow gehören, löschen Sie sie aus der app-config.js. Die Routen werden aus den registrierten Datenquellen erzeugt, eine nicht registrierte Quelle hat keine Route.

  • Origin-Allowlist konfigurieren, falls Sie die Quellen nutzen.

    Der Fix schließt im Zweifel: Ohne konfigurierte Allowlist wird ein authentifizierter Cross-Origin-Abruf abgewiesen. Setzen Sie die Allowlist ausdrücklich auf die vorgesehenen Origins, statt sie leer zu lassen und das Verhalten im Produktivbetrieb zu entdecken.

  • Die eigene Origin auf Open Redirects prüfen.

    Jeder Endpunkt auf der Origin des Viewers, der per 302 auf einen vom Aufrufer übergebenen Parameter weiterleitet, öffnet diese Angriffsklasse über eine Same-Origin-URL erneut. Prüfen Sie Auth-Proxies, Ingress-Regeln, SSO-Portale und OIDC-Callback-Handler auf parametrisierte Weiterleitungsziele.

  • Access-Token für den Viewer kurzlebig und Audience-gebunden halten.

    Ein Token, der nur für das Imaging-Backend gilt und in Minuten abläuft, begrenzt den Wert eines einzelnen abgegriffenen Credentials. Prüfen Sie, ob Ihr IdP dem Viewer einen Token ausstellt, der auch andernorts akzeptiert wird.

Bewertung im Detail

AV:NDer Angriff wird als Link über einen beliebigen Kanal zugestellt, der den Klinik-Nutzer erreicht, und das Credential verlässt den Browser über das Netzwerk.AC:LKeine Race Condition, keine zeitliche Abhängigkeit, keine umgebungsspezifische Voraussetzung. Die betroffenen Datenquellen sind in der ausgelieferten Standardkonfiguration vorhanden, und der Angreifer benötigt lediglich einen Webserver mit permissiven CORS-Headern, also einen Server unter seiner eigenen Kontrolle.PR:NDer Angreifer besitzt weder ein Konto noch ein Credential im Zielsystem.UI:REin bereits authentifizierter Nutzer muss den Link öffnen. Ein Klick, keine weitere Interaktion.S:CDie verwundbare Komponente ist der Viewer im Browser; die Auswirkung trifft das Imaging-Backend, eine eigene Sicherheitsautorität. Der abgegriffene Token wird dort eingelöst.C:HDer abgegriffene Token eröffnet die Bildgebungsstudien, auf die der Nutzer zugreifen kann, und damit Patientendaten.I:LDer Angreifer bestimmt zudem, was der Viewer in dieser Sitzung darstellt, angezeigte Studieninhalte lassen sich also austauschen. Die Integrität des Backends ist durch das begrenzt, was der abgegriffene Token autorisiert.A:NKeine Auswirkung auf die Verfügbarkeit.

Referenzen

So können wir helfen

Wer wir sind

Die Sicherheitsforscher hinter diesem Sicherheitshinweis.

Dr. Simon Weber Profile

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
Volker Schönefeld Profile

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.