Leider hat sich gezeigt, dass Frontend-Webanwendungen schwer abzusichern sind. Das Cross-Site-Scripting (XSS; dt.: Webseitenübergreifendes Skripting) ist jetzt schon seit fast zwei Jahrzehnten eine Plage für Webanwendungen. Mit den aktuellen JavaScript-Frameworks ist es zwar ein bisschen besser geworden, aber es gibt immer noch viele potenzielle XSS-Angriffsvektoren in modernen Anwendungen. Auf dieser Checkliste finden Sie weitere Informationen über die Sicherung von React- und Angular-Anwendungen.
War das dann alles? Nun, ja und nein!
Vom Sicherheitsstandpunkt aus gesehen, ist es praktisch unmöglich, Token in einer Frontend-Webanwendung abzusichern. Ein schädlicher JavaScript-Code hat die gleiche Handhabe wie auch die Anwendung selbst. Wenn also die Anwendung auf Token zugreifen kann, kann der Schadcode das ebenfalls. Sicherheitsmuster, wie das Verstecken von Aktualisierungstoken in einem Web Worker, können hilfreich sein, sind aber auf Dauer keine Lösung für die zuvor diskutierten Szenarien.
Nicht sensible Frontend-Anwendungen können sich konkret auf Aktualisierungstoken mit einer Aktualisierungstoken-Rotation verlassen. Eine Anwendung für Restaurantbewertungen kann als nicht sensibel gelten, was die Wirkung eines erfolgreichen XSS-Angriffs verringert. Bei sensiblen Anwendungen ist dies nicht zu empfehlen. Äußerst sensibel sind beispielsweise Anwendungen, die personenbezogene Informationen, Daten aus dem Gesundheitswesen oder Finanzvorgänge verarbeiten. Bei solchen Anwendungen hat ein erfolgreicher XSS-Angriff erhebliche Auswirkungen.
Deshalb sollten sensible Frontend-Anwendungen nicht mit Token im Browser arbeiten. Sie können stattdessen auf ein BFF-Muster (Backend für Frontend) zurückgreifen, bei dem die Verarbeitung von Token auf eine minimalistische serverseitige Komponente verlagert wird. Die nachstehende Abbildung veranschaulicht das Konzept eines BFF-Musters:
BFFs werden traditionell dazu verwendet, verschiedene APIs zu einer einzigen kohärenten API zusammenzufassen, um eine Client-Anwendung zu bedienen. In unserem Szenario übernimmt das BFF-Muster außerdem eine geringe Sicherheitsfunktion. Es nimmt Anfragen von der Client-Anwendung entgegen, ergänzt sie mit OAuth 2.0-Zugriffstoken und leitet die Anfrage an die API weiter. Ebenso wird jede Antwort von der API an die Client-Anwendung weitergeleitet.
Wie funktioniert ein BFF-Muster in der Praxis?
Einzelheiten zum BFF-Muster
In diesem Szenario wird das BFF-Muster zu einer OAuth 2.0-Client-Anwendung. Da das BFF-Muster auf einem Backend-System läuft, kann es als vertraulicher Client konfiguriert werden. Das BFF ist der Client, d. h. es initialisiert den OAuth 2.0-Flow, der im Browser des Benutzers läuft. Nach dem ersten Schritt des Flows erhält das BFF einen Autorisierungscode, den es im Rahmen einer Client-Authentifizierung mit dem Autorisierungsserver gegen Token eintauscht. Mit dem Zugriffstoken kann das BFF-Muster die API-Anfragen weiterleiten. Bei Bedarf kann das BFF mit dem Aktualisierungstoken einen neuen Zugriffstoken erhalten. Beachten Sie, dass für die Verwendung des Aktualisierungstokens eine Authentifizierung des BFF-Musters gegenüber dem Autorisierungsserver erforderlich ist.
Das BFF-Muster wird von Hunderten oder sogar Tausenden von Client-Instanzen gemeinsam genutzt, von denen jede im Namen eines anderen Benutzers agiert. Das BFF behält mit einer Cookie-basierten Sitzung den Überblick über diese Nutzer. Das BFF kann diese Sitzung auf dem Server behalten (z. B. in einem einfachen Datenspeicher), kann sie aber auch an den Client weitergeben (z. B. in einem verschlüsselten Sitzungsobjekt). Ersteres führt zu einem zustandsabhängigen BFF-Muster, während bei letzterem das BFF zustandslos wird. Beide Ansätze sind machbar.
Beachten Sie, dass das BFF-Muster die Best Practices der Cookie-Sicherheit befolgen muss, um die Sicherheit des Cookies zu gewährleisten. Konkret bedeutet dies, dass zum Setzen eines Cookies mit dem Namen „MyBFFCookie“ der folgende Header verwendet werden muss: Set-Cookie: __Host-MyBFFCookie=…; Secure; HttpOnly; SameSite. Weitere Informationen über die Cookie-Sicherheit.
Wenn der Client eine Anfrage sendet, verwendet das BFF-Muster die Sitzungsinformationen in der Anfrage, um die Token des Benutzers abzurufen. Wenn der Zugriffstoken noch gültig ist, kann das BFF die Anfrage direkt weiterleiten. Sollte der Zugriffstoken abgelaufen sein, verwendet das BFF den Aktualisierungstoken des Benutzers, um einen neuen Zugriffstoken zu erhalten, bevor es die Anfrage weiterleitet.
Es sollte noch erwähnt werden, dass sich aus Sicht des Nutzers nichts ändert. Die Benutzererfahrungen einer Frontend-Client-Anwendung und einer Frontend-Anwendung, die von einer BFF unterstützt wird, sind identisch.
Die Vorteile des BFF-Musters
Ein Backend-für-Frontend-Muster bietet erhebliche Sicherheitsvorteile gegenüber browserbasierten Anwendungen. Es fungiert als OAuth 2.0-Client und kann somit bewährte Sicherheitsverfahren für vertrauliche Clients zum Einsatz bringen. Dies bedeutet konkret Folgendes:
Das BFF-Muster muss sich gegenüber dem Autorisierungsserver authentifizieren, wenn ein Autorisierungscode oder ein Aktualisierungstoken eingetauscht wird.
Das BFF kann sich auf solide schlüsselbasierte Authentifizierungsmechanismen stützen (z. B. mutual TLS).
Das BFF kann Sender-Constrained-Zugriffstoken und Sender-Constrained-Aktualisierungstoken verwenden.
Zugriffstoken und Aktualisierungstoken werden im Browser niemals offenbart.
Aber was wäre mit einem potenziell schädlichen JavaScript Code, der in der Frontend-Anwendung läuft? In diesem Szenario kann der Schadcode nicht mehr auf die Token zugreifen, da sie nur für die BFF verfügbar sind. Durch Cookie-Sicherheitsmaßnahmen (d. h. das HttpOnly-Attribut) wird verhindert, dass der Schadcode die Sitzung mit dem BFF-Muster stiehlt.
Der Schadcode kann jedoch das Verhalten der Client-Anwendung verändern. Konkret kann der Angreifer einen Session-Riding-Angriff durchführen, indem er bösartige API-Aufrufe über die BFF sendet. Solche API-Aufrufe können nicht von legitimen Anfragen des Clients unterschieden werden.
Allerdings hat das BFF hier die volle Kontrolle. Daraus folgt, dass ein BFF-Muster die API-Fläche beschränken kann, indem es den Client daran hindert, auf bestimmte Endpunkte zuzugreifen. Darüber hinaus kann das BFF mit Mustern zur Verkehrsanalyse arbeiten, um verdächtiges Verhalten aufzudecken. Beispiele hierfür sind unter anderem das Erkennen einer verdächtig großen Anzahl von Vorgängen oder das Beobachten einer unerwarteten Reihenfolge sensibler Vorgänge.
Schließlich bleibt zu bedenken, dass ein BFF nichts unternimmt, um eine Ausführung des Schadcodes zu verhindern. Der Angreifer kann nach wie vor sensible Daten auslesen oder Social-Engineering-Angriffe auf den Benutzer durchführen. Die einzige Möglichkeit, derartige Angriffe zu verhindern, ist die Einhaltung strenger Richtlinien zur sicheren Programmierung der Frontend-Anwendung.