OWASP Top 10 API Sicherheitsprobleme
05.10.2020, Christian Mäder

Die OWASP sammelt und kategorisiert typische Sicherheitsprobleme in Websoftware. Im Dezember 2019 veröffentlichten sie das erste Mal eine Sammlung von solchen Problemen, die häufig bei der Umsetzung von Web APIs angetroffen werden. In diesem Artikel wird jedes dieser 10 Probleme kurz zusammengefasst.

Beim Entwickeln modernen Software spielen Web APIs eine immer wichtigere Rolle. Single Page Webapplikationen, wie sie häufig in Enterprise- und B2B-Umgebungen angetroffen werden und meist mit Technologien wie Angular und VueJS entwickelt sind, setzen ausschliesslich auf Web APIs. Deshalb beschäftigen wir uns immer stärker mit den Sicherheitsaspekten solcher – und anderer – APIs.

Die Titel der einzelnen Probleme sind direkt dem OWASP Dokument entnommen und wurden nicht übersetzt, da die Titel von den Autoren sehr bewusst gewählt wurden und eine Übersetzung die ursprüngliche Bedeutung verwässern könnte.

Problem 1: Broken Object Level Authorization

Bei einer typischen Abfrage einer API wird eine bestimmte Resource eines bestimmten (manchmal externen) Services abgefragt oder es werden Aktionen auf einer solchen Resource vorgenommen. Jeder Service, der solche APIs bereitstellt, sollte überprüfen, ob die jeweiligen Benutzer*innen, die eine solche Anfrage durchführend, dies überhaupt dürfen.

Als Beispiel soll ein Service dienen, der von verschiedenen Benutzer*innen genutzt wird. Alle Benutzer*innen können ihr eigenes Profil über /user/{userId} abrufen. Also ein Benutzer mit der ID 11 darf auf die Informationen unter /user/11 zugreifen. Die Benutzerin mit der ID 12 darf hingegen die Informationen unter /user/11 nicht sehen.

Es gibt jedoch Services, die keine solche fein-granulare Überprüfung der Berechtigungen vornehmen. Diese verlangen dann zum Beispiel einfach eine gültige Anmeldung, um auf /user/* zuzugreifen. Im beschriebenen Fall wäre dies aber falsch, und genau dies ist, was dieses erste Problem beschreibt.

Das Problem 5 weiter unten ist ähnlich wie dieses.

Problem 2: Broken User Authentication

Oft dürfen nur authentifizierte Benutzer*innen auf eine API zugreifen. Dann müssen sich Benutzer*innen irgendwie bei der API anmelden. Dieses zweite Problem beschreibt typische Szenarien, die bei der Implementation oder Konfiguration des Authentifizierungs-Mechanismuses nicht bedacht wurden.

Zu solchen Schwachstellen-Szenarien gehören:

  • Unbefugte Benutzer*innen könnten eine Liste gültiger Anmeldedaten von anderen Services besorgen, beispielsweise weil es auf einen solchen anderen Service einen erfolgreichen Angriff gegeben hat oder weil solche Informationen mittels Phishing (eines andern Services) ergattert wurden. Nun werden einfach alle diese Anmeldedaten durchgeprüft, in der Hoffnung, dass es bei einigen Benutzer*innen des eigentlichen Services eine Überschneidung mit den Anmeldeinformationen des anderen Services gibt. Diesem Vorgehen sagt man Credential Stuffing und dem kann man entgegen wirken, in dem die Anzahl der Anmeldeversuche pro Zeiteinheit reguliert wird oder beispielsweise nach einer bestimmten Anzahl von Fehlversuchen ein CAPTCHA gelöst werden muss. Auch ein zweiter Authentifizierungs-Faktor hilft massgeblich, die Erfolgschancen eines solchen Angriffs erheblich zu reduzieren.
  • Es gibt auch noch den klassischen Bruteforce Angriff. Dabei werden einfach alle Kombinationen für Benutzernamen und/oder Passworte durchprobiert. Hierbei ist es wichtig, dass ein gültiges Benutzer*innenkonto nach einer bestimmten Anzahl von fehlgeschlagenen Anmeldeversuchen für eine bestimmte Zeit gesperrt wird. Oder dass eine Mehrfaktor-Authentifizierung verlangt wird.
  • Wenn es keine Regeln für starke Passworte gibt – also ungenügende Mindestanforderung an die Passwortlänge und keine Überprüfung, ob ein bestimmtes Passwort bereits in einer Liste von öffentlich bekannten Passworten ist (beispielsweise durch Nutzung der Have I Been Pwned API) – dann können solche Passworte sehr einfach erraten werden. Und sollte es sogar zu einem erfolgreichen Angriff auf die Passwort-Datenbank kommen, dann können solche Passworte auch sehr schnell zurückgerechnet werden.
  • Sensitive Informationen in der URL müssen unbedingt vermieden werden, weil solche Informationen oft als Nebenprodukt irgendwo unbeabsichtigt gespeichert werden. Beispiele hierfür sind der Verlauf des Browsers oder auch der Verlauf des Betriebssystem-Terminals.
  • Wenn das Backend des API Dienstes nicht überprüft, ob ein bestimmtes API Token noch gültig ist, kann ein eigentlich bereits ungültiges Token von unbefugten Benutzer*innen verwendet werden. Dies kann zum Beispiel geschehen, wenn sich legitime Benutzer*innen von einem Service abmelden; das Token jedoch ursprünglich für eine längere Zeit ausgestellt wurde, damit sich die Benutzer*innen nicht regelmässig in der App neu Authentifizieren müssen. Eine unbefugte Person erhält nun Zugriff auf ein solches Token, das zeitlich noch gültig ist, aber vom Server eigentlich schon für ungültig erklärt wurde. So ein altes Token konnte beispielsweise von einem Occassion-Gerät rekonstruiert werden. Das Token sollte zwar eigentlich nicht mehr für den Zugriff auf das API genutzt werden können. Dazu muss aber der API-Dienst stets jedes Authentifizierungs-Token beim Authentifizierungs-Server überprüfen. Wird dieser Schritt vernachlässigt, kann das für ungültig erklärte Token trotzdem genutzt werden, weil die darin gespeicherte Gültigkeit noch nicht abgelaufen ist.
  • Wenn der API-Service schwache Tokens zur Authentifizierung verwendet, also zum Beispiel solche, die nicht signiert sind oder nur mit einem schwachen Algorithmus (wie SHA-1), dann können fälschlicherweise gültige Token von unbefugten Benutzern mit überschaubarem Aufwand generiert werden.
  • Wenn unverschlüsselte Anmeldedaten über unverschlüsselte Verbindungen versendet werden, dann können diese vergleichsweise einfach von unbefugten Benutzer*innen abgehört werden.

Ein ähnlich gelagertes Beispiel besteht mit dem HTTP Header X-Forwarded-For. Oft werden solche Header am Rand des eigenen Systems nicht aus den eingehenden Requests entfernt. Trotzdem werden diese dann im Backend eingesetzt, um beispielsweise IP-basierte Authorisierung zu implementieren. Wenn nun Absender*innen von API-Requests den X-Forwarded-For Header (oder andere Header mit ähnlichem Zweck) selber beeinflussen können, dann können diese so die Sicherheitsmassnahmen umgehen.

Problem 3: Excessive Data Exposure

Dieses dritte Problem legt das Augenmerk auf unbeabsichtigt gesendete Informationen oder solche, die mit unbeabsichtigt hohem Detailgrad bereitgestellt werden.

Als Beispiel soll eine Dating App dienen. Die möglichen Dating-Kandidat*innen werden von der API zurückgeliefert. Als Information zur Berechnung der Distanz dienen die Koordination des aktuellen Standorts der Kandidat*innen. In der App wird der genaue Ort der Kandidat*innen gar nicht dargestellt. Obwohl die Information im UI gar nicht dargestellt wird, können technisch versierte Benutzer*innen die Information trotzdem aus der API auslesen und so den genauen aktuellen Standort der anderen Nutzer*innen dieser App rekonstruieren. Die Standortinformation ist also unnötig genau. Es würde auch reichen, wenn der Standort nur ungenau (bsp. auf 100m genau) gesendet würde. Oder wenn der Server gleich selber die Berechnung der Distanz übernehmen würde und nur dies der Client-Applikation zurückmelden würde.

Problem 4: Lack of Resources & Rate Limiting

API Requests benötigen Server Ressourcen, die durch die Betreiber*innen bereitgestellt werden müssen. Gerade auf Cloud-Plattformen werden diese Ressourcen sehr direkt mit Geld aufgewogen. Aber auch herkömmliches Hosting ist selten gratis.

Es ist deshalb im Eigeninteresse aller API Service Betreiber*innen, die zur Verfügung stehenden Ressourcen zu begrenzen. Selbst wenn die Kund*innen pro Request bezahlen, macht es Sinn, diese vor sich selber zu schützen. Denn schnell ist in einem Script etwas verändert, was versehentlich viele Requests auf den Service auslöst.

Dinge, die dazu beachtet werden sollten:

  • Eine Zeitbeschränkung für die Ausführung definieren, nach der einzelne Requests abgebrochen werden. Dies verhindert, dass wenige Requests das ganze System blockieren.
  • Die maximal erlaubte Grösse eines Requests definieren, damit eine Client-Anwendung nicht unbeschränkt viele Daten senden kann.
  • Die Anzahl der Requests pro Zeiteinheit für jede*n Benutzer*in und für jede Resource definieren, beispielsweise 120 Requests pro Minute (pro Benutzer und pro Resource oder sogar eine Kombination davon).
  • Die Grösse der Antworten limitieren. Dazu wird üblicherweise Paging eingesetzt. Es wird also nur ein Teil der vorhandenen Daten pro Request zurückgesendet wird. Weitere Daten können – bei Bedarf – in weiteren Requests angefordert werden.
  • Ein Limit setzen, wie viel RAM pro Request (oder mindestens pro Service) genutzt werden kann.
  • Ein Limit definieren, wie viele Dateien ein Request (oder mindestens ein Service) gleichzeitig öffnen darf. Dies verhindert, dass Hintergrundprozesse (wie eine Datenbank) keine Dateien mehr öffnen können.
  • Ein Limit definieren, wie viele Prozesse pro Request (oder mindestens pro Service) parallel gestartet werden können. Dies verhindert, dass Hintergrundprozesse (wie eine Datenbank) keine eigenen neuen Prozesse starten können.

Einige dieser Limiten und Empfehlungen können beim Einsatz von Container-Technologie (wie Docker oder Kubernetes) relativ einfach definiert werden. Andere können oft mittels eines API Gateways oder einer Service Mesh Software umgesetzt werden. Und einige müssen hingegen direkt im Code der Applikation umgesetzt werden.

Problem 5: Broken Function Level Authorization

Dieses Problem beschreibt, dass Benutzer*innen nur Zugriff auf die API Endpoints haben sollten, für die sie auch zugelassen sind. Beispielsweise gibt es wieder den Benutzer mit der ID 11, der Informationen zu seinem Profil unter /user/11 abrufen kann. Er sollte jedoch keinen Zugriff auf die API /users haben, wo alle Benutzer*innen aufgelistet werden.

Das Problem ist ganz ähnlich dem Problem 1 vom Anfang. Dasjenige Problem beschreibt jedoch den Zugriff auf den gleichen Endpoint, also zum Beispiel /user/11 vs. /user/12. Das vorliegende Problem hingegen beschreibt den Zugriff auf verschiedene Ressourcen, also hier /user/11 vs. /users.

Problem 6: Mass Assignment

Dieses Problem beschreibt, dass Benutzer*innen unter Umständen Informationen über die API verändern können, die sie eigentlich verändern dürften.

Typischerweise haben zum Beispiel Benutzer*innen in Applikationen eine eindeutige ID. Und während Benutzer*innen nun ihre eigene E-Mail Adresse anpassen dürfen, sollte es ihnen nicht möglich sein, ihre ID zu verändern.

Problem 7: Security Misconfiguration

Dieses Problem beschreibt typische Sicherheitsprobleme bei Software. Dazu gehören:

  • Sicherheits-Updates (auf jeglicher Ebene, z.B. Betriebssystem, Runtime, Entwicklungs-Frameworks, API Gateways, etc.) sind nicht installiert.
  • Es sind nicht genutzte Funktionen aktiv. Zum Beispiel, dass Apache HTTPD eine Liste von allen Dateien in einem Ordner erstellt, wenn keine index.html (oder entsprechende) Datei besteht.
  • Die Verbindung ist nicht ausreichend verschlüsselt, zum Beispiel mit TLS oder DTLS. Oder die Verschlüsselung ist schlecht konfiguriert, beispielsweise in dem TLS 1.1 und ältere Protokollversionen noch nicht deaktiviert sind.
  • Sicherheitsfunktionen werden nicht genutzt oder sind falsch konfiguriert. Dazu zählt zum Beispiel, dass die sog. Security Headers in HTTP nicht konfiguriert sind, oder dass eine all zu grosszügige Cross-Origin Resource Sharing (CORS) Policy genutzt wird.
  • Sensitive Informationen können sich auch in Logdateien befinden, die fälschlicherweise öffentlich zugänglich sind. Oder auch in Fehlermeldungen (allenfalls inkl. Stack Traces), die den Benutzer*innen angezeigt werden und interne Informationen preisgeben.

Problem 8: Injection

Dieses Problem beschäftigt sich mit Daten ausserhalb des Systems, die beispielsweise durch Benutzer*innen oder Drittsysteme bereitgestellt werden. Solchen Informationen darf grundsätzlich nicht vertraut werden. Es müssen Massnahmen getroffen werden, dass solche Informationen keine applikationsinterne Befehle modifizieren können. Modifikationen dieser Art werden generell als Injections bezeichnet. Es gibt verschiedene Arten solcher Injections, zum Beispiel SQL Injections, Betriebssystem-Kommando Injections oder HTML injections (auch bekannt als Cross Site Scripting, XSS).

Konkrete Beispiele sind:

  • Wenn Einträge in eine Datenbank gespeichert werden, dann muss darauf Acht gegeben werden, dass die SQL-Befehle nicht durch Inhalte von Dritten verändert werden können.
  • Wenn Informationen für den Webbrowser vorbereitet werden, dann muss darauf Acht gegeben werden, dass kein HTML oder JavaScript aus Drittquellen unverändert auf die Webseite ausgegeben wird.
  • Wenn Informationen in ein JSON-Objekt verpackt werden, um ein Drittservice aufzurufen, dann muss darauf geachtet werden, dass die Informationen nicht die JSON-Objektstruktur verändern können.
  • HTTP Header wie X-Forwarded-For sollte generell vom ersten Server, der einen HTTP Request empfängt, entfernt werden. Beispielsweise könnte es sein, dass der Inhalt eines Header als Parameter für eine Script genutzt wird. Werden solche Header nicht entfernt – und ohne weitere Massnahmen – könnten in diesem Beispiel Benutzer*innen beliebige Befehle auf den entsprechenden Servern ausführen.

Problem 9: Improper Assets Management

Dies ist eher ein organisatorisches Problem, was die Behandlung nicht unbedingt einfacher macht.

Es beschreibt, dass Organisationen oft keine gute Übersicht über die API(-Versionen) haben, die sie aktuell betreiben. Auch wissen sie manchmal nicht so genau, wie sie die unterschiedlichen API schützen.

An dieser Stelle ein Beispiel aus dem ursprünglichem OWASP Dokument: Es wird beschrieben, dass ein bestimmtes API über eine URL erreichbar ist, zum Beispiel myservice.example/api. Parallel dazu wird eine Entwicklungsversion unter beta.myservice.example/api betrieben. Im Gegensatz zur produktiven API ist die Entwicklungsversion nicht durch das gleiche Anti-Bruteforce Gateway geschützt und wir auch nicht gleich gut überwacht. Trotzdem enthält es ähnliche Daten wie das produktive API, weil hin und wieder die produktiven Benutzer*innen in die Entwicklungsversion importiert werden. Angreifer*innen, die dies herausfinden, können nun ihren Angriff auf der Entwicklungsumgebung durchführen. Sobald die entsprechenden Anmeldedaten herausgefunden wurden, kann damit auf die produktive Umgebung zugegriffen werden.

Problem 10: Insufficient Logging & Monitoring

Meiner Erfahrung nach entsteht dieses Problem vor allem dann, wenn der Betrieb einer Applikation (oder in diesem Fall der API) nur als Nebenleistung am Ende des Projekts angesehen wird. Das Entwicklungsteam sieht es nicht als seine Aufgabe an, an den Betrieb der Applikation zu denken und es wurde auch nicht dazu verdonnert, sich bereits früh in der Entwicklung mit dem Betriebspersonal abzusprechen.

Logdateien sind besonders wichtig, wenn im Nachhinein herausgefunden werden soll, wieso etwas so abgelaufen ist, wie es abgelaufen ist. Dies gilt insbesondere, wenn eine laufende Attacke abgewehrt werden soll oder eine bereits abgelaufene Attacke nachvollzogen werden muss. Inkorrekt konfiguriertes Logging kann jedoch einen Server auch lahmlegen. Beispielsweise wenn alles auf dem TRACE Level geloggt wird und sich so die Disk füllt. Oder nur schon, wenn das so viele Logdaten generiert und damit so viele IO-Operationen auf dem Server verursacht, dass andere Prozesse ihre Dateien nicht mehr oder nur stark verzögert lesen oder schreiben können.

Schlusswort

Viele dieser Probleme werden nicht bewusst geschaffen und das Verhindern dieser ist eigentlich gesunder Menschenverstand. Aber dazu müssen die Entwickler*innen und Betreiber*innen schon einmal von den entsprechenden Problemen gehört haben. Zumindest wir sind uns diesen Problemen bewusst und versuchen, diese bei unseren Applikationen zu vermeiden.

Ich kann allen Leser*innen empfehlen, sich auch mit dem Originaldokument zu befassen. Es enthält weitere illustrierende Beispiele sowie die detaillierten Beschreibungen jedes Problems. Das Originaldokument kann kostenlos vom entsprechenden GitHub Repository bezogen werden.