CORS ist nicht dazu gedacht einen API Endpunkt abzusichern

Vor ein paar Tagen bin ich auf diesen Artikel von Designkojo gestoßen. Der Autor zeigt dort, wie man mit einer Vue.js App auf ein Drupal System im Backend zugreift. Für die Authentifizierung verwendet er einen API-Key - und das finde ich gefährlich. Hier ist warum.

Vorab möchte ich kurz das in dem Artikel vorgestellte Setup erklären: Das Backend wird durch Drupal bereitgestellt. Drupal hat seit der Version 8 eine JSON API im Kern integriert, die sich einfach über die Modulverwaltung aktivieren lässt. Damit ist es nun möglich auf alle Entitäten (z. B. User, Nodes, etc.) zuzugreifen und diese via GET Request auszulesen.

Es ist außerdem möglich hier einen Schreibzugriff zu aktivieren.

Die JSON API orientiert sich dabei an den definierten Berechtigungen im Drupal-System. Hat ein User also keinen Lesezugriff auf einen Inhalt, dann kann dieser Inhalt auch nicht über die JSON API geladen werden. Gleiches gilt hier für den Schreibzugriff: eine Entität kann nur erstellt, geändert oder gelöscht werden, wenn der zugreifende Benutzer über die entsprechende Berechtigung verfügt.

Er greift mit Vue.js auf das Drupal Backend zu

Aber zurück zum Artikel: Der Autor beschreibt hier, wie er in einer clientseitigen Vue App auf das Drupal-Backend zugreift um Inhalte zu erstellen. In dem gezeigten Beispiel erstellt er einen Inhalt (in Drupal auch Node genannt) vom Typ “Artikel”.

Nun wird es interessant: Er hat sein Drupal System so konfiguriert, dass nur angemeldete Benutzer Artikel erstellen können. Um einen Artikel über die JSON API erstellen zu können, muss er sich also zunächst authentifizieren.

Es gibt verschiedene Wege sich in Drupal zu authentifizieren. Der übliche Weg ist über einen Benutzernamen und ein Passwort. Dies geht nicht nur direkt über das Frontend, sondern auch über die API. Hier liefert Drupal nach erfolgreicher Authentifizierung eine Session ID zurück, die man zwischenspeichern und für alle weiteren Anfragen verwenden kann.

Authentifizierung über API Key statt Benutzername & Passwort

Der Autor nutzt nun aber einen anderen Weg: er Installiert das Key auth Modul. Dieses erstellt für jeden Benutzer im Backend einen eigenen API Key, der für die Authentifizierung an der API verwenden werden kann. Dazu muss bei jedem Aufruf der Parameter “api-key” mit dem Schlüssel als Wert übergeben werden. Hiermit ist der Benutzer automatisch in Drupal authentifiziert und verfügt über alle zugewiesenen Rechte.

Der Fehler: API Key im Quellcode

Und hier unterläuft ihm ein gefährlicher Fehler: er integriert den API Key aus dem Drupal Backend in seiner Vue.js App um sich darüber an der API zu authentifizieren und Inhalte erstellen zu können.

Warum ist das ein Fehler?

Eine Vue.js App wird clientseitig, also im Browser des Anwenders ausgeführt. Das bedeutet, dass der Quellcode vom Server direkt an den Browser gesendet wird. Hier hat nun der Anwender vollen Zugriff auf den Quellcode und kann auch den darin enthaltenen Auth Key auslesen.

Aber ich habe doch den Auth Key in einer Umgebungsvariable gespeichert

Eine Umgebungsvariable wird während des Build-Prozesses der Web-App in den Quellcode geschrieben. Umgebungsvariablen sind dazu gedacht (wie der Name schon sagt), unterschiedliche Variablen für unterschiedliche Umgebungen bereitzustellen. So kann man zum Beispiel in einer Entwicklungsumgebung andere Variablen nutzen, als in einer Staging- oder eine Produktivumgebung. Möchte man zum Beispiel den Zahlungsanbieter Stripe integrieren, dann kann man in der Entwicklungs- und Staging-Umgebung den Testmodus von Stripe verwenden, während man in der Produktiv-Umgebung den Livemodus von Stripe nutzt.

Aber die CORS Einstellung sorgt doch dafür, dass ich nur von einer bestimmten Domain auf das Backend zugreifen kann

Nein! CORS ist eine Implementierung im Browser und ist dazu gedacht den Anwender vor schädlichen Anwendungen zu schützen, indem es sicherstellt, dass die Ressource im Browser nur auf bestimmte Endpunkte zugreifen darf.

Diese Browser-Implementierung kann jederzeit umgangen werden. Zunächst liegt es am Browser selbst: ist CORS nicht oder nicht sauber integriert, dann funktioniert es nicht. Der Server sendet einen CORS Header und es ist in der Hand des Browser zu entscheiden wie damit umgegangen wird. Dies ist kein Sicherheitselement, denn es liegt nicht in der Hand des Servers hier einen Zugriff zu verweigern, sondern einzig beim Browser.

Ein mögliches Angriffsszenario

Ein Angreifer kann über den Quellcode der Web-App an den API Key gelangen und diesen zum Beispiel über einen cURL Request verwenden um direkt auf die API Ressourcen des Backends zuzugreifen. Bei cURL greift kein CORS, der Angreifer hat also direkt einen Zugriff mit den vollen Rechten des Users.

Würdest du einen Benutzernamen mit Passwort veröffentlichen?

Ein API Key ist eine Alternative zum Benutzernamen und Passwort. Niemand würde auf die Idee kommen, einen Benutzernamen und ein Passwort in den Quellcode einer Web-App abzuspeichern.

Es gibt aber Szenarien wo dies dennoch möglich wäre. Zum Beispiel, wenn der API Key eingeschränkte Rechte hätte. Dies ist bei einigen Diensten durchaus der Fall (z. B. bei einigen Google Diensten, wie Google Maps).

Das in dem Artikel beschriebene “Key auth” Modul schränkt jedoch die Rechte des Keys nicht ein. Es ist lediglich eine Alternative zum Benutzernamen & Passwort.

Warum ich den Artikel von Designkojo gefährlich finde

Ich bin auf den Artikel gestoßen, weil ich nach Wegen gesucht habe eine Vue APP an einem Drupal Backend zu authentifizieren. Der Artikel von Designkojo war einer der ersten, die in der Suchmaschine oben auftauchten. Noch dazu hat der Artikel Charme: er ist gut und kompakt beschrieben. Gerade für Anfänger ist der beschriebene Weg leicht und schnell umzusetzen. Und er suggeriert: die Authentifizierung via API Key sein ein gängiger Weg. Und hier wirds gefährlich: gerade unerfahrene Entwickler können darauf hereinfallen und glauben dies sei eine übliche Methode um sich mit einer Web-App an einem Backend zu authentifizieren.

Ich habe über Twitter mit dem Autor des Artikels Kontakt aufgenommen und meinen Standpunkt erklärt. Wir haben eine längere Diskussion darüber geführt und er hat meinen Punkt verstanden. Er hat seinen Artikel zwar um einen entsprechenden Warnhinweis ergänzt, ihn aber dennoch so stehen gelassen. 
 

 

Kommentare

Lass uns ein paar Gedanken teilen!

Schließe Dich einer wachsenden Gemeinschaft von freundlichen Lesern an. Jeden Sonntag teile ich meine Gedanken über rationales Denken, Produktivität und das Leben.