Unabhängig, aber sicher: Serverless-Security

Das Verteilen von Anwendungslogik auf zahlreiche Serverless Functions skaliert gut, bringt aber neue Herausforderungen beim Absichern der Anwendung mit sich.

Lesezeit: 14 Min.
In Pocket speichern
vorlesen Druckansicht Kommentare lesen 8 Beiträge

(Bild: NicoElNino/Shutterstock.com)

Von
  • Lars Röwekamp
Inhaltsverzeichnis

Serverless-Architekturen ermöglichen es, komplexe Anwendungssysteme aufzubauen, ohne sich um das Management der Infrastruktur kümmern zu müssen. Aspekte wie Verfügbarkeit, Provisionierung und Skalierung übernimmt der Cloud-Provider. Der Fokus der Anwendungsentwicklung liegt auf der Implementierung der Fachlichkeit in Form losgelöster Funktionen. Unter dem Strich ist der Ansatz zudem kosteneffizient – vorausgesetzt das Entwicklungsteam hat bei Architektur und Softwaredesign seine Hausaufgaben gemacht.

Dank Managed Runtime Model gehören Maintenance, Updates und Patches für den gesamten (virtualisierten) Hardware- und Software-Stack der Vergangenheit an. Das Team kann seinen Fokus zu 100 Prozent auf die Umsetzung der Businesslogik der Anwendung legen.

Soweit die Theorie. In der Praxis ist es am Ende nicht so trivial, wie es die Cloud-Provider gerne anpreisen. Das Aufspalten einer monolithischen Anwendung in viele kleine Funktionen, die wiederum stark verteilt, lose gekoppelt und asynchron mit anderen Funktionen oder Cloud-Komponenten interagieren, führt zu einem auf vielen Ebenen äußerst komplexen System, das nicht einfach zu beherrschen ist. Folgende Abbildung zeigt beispielhaft ein Serverless-Szenario zum Speichern eines Bildes, inklusive zugehöriger Beschreibung in einem virtuellen Reisetagebuch:

Das Speichern eines Bildes löst mehrere Serverless-Funktionen aus (Abb. 1).

In der Welt der Serverless Functions besteht eine Anwendung nicht selten aus Hunderten von Funktionen, von denen jede für sich genommen relativ trivial ist. Ihr Zusammenspiel dagegen ergibt ein hochkomplexes und – "dank" Cloud-Umgebung – fragiles Gesamtsystem.

Die Aktivierung der einzelnen Funktionen erfolgt in der Regel durch dedizierte Events, inklusive zugehöriger Payloads. Nach getaner Arbeit, beispielsweise dem Ablegen von Bildern in einem Object Store oder dem Speichern von Daten in einer NoSQL-Datenbank, triggern sie nicht selten weitere Funktionen oder Cloud-Komponenten – ebenfalls durch passende Events. Am Ende ergibt sich ein lose gekoppeltes, asynchron arbeitendes System. Eine Freude für jeden Architekten.

Um für ein solches System eine hinreichende Sicherheit zu garantieren, ist ein Umdenken gefragt. Die Angriffsfläche ist deutlich breiter als bei einer monolithischen Anwendung, die einzelnen Angriffspunkte verteilen sich auf eine Vielzahl unterschiedlicher Komponenten.

Trotz der vielen kleinen, oft trivialen Funktionen spielt insbesondere die Gesamtsicht auf das System bei der Betrachtung sicherheitsrelevanter Aspekte eine entscheidende Rolle. Welche Angriffspunkte bieten die einzelnen Komponenten des Systems und welche zusätzlichen Angriffspunkte ergeben sich durch deren Zusammenspiel? Welche Daten verarbeitet das System? Wie sensibel sind die Daten und welchen Wert haben sie? Wie hoch wäre der Schaden, wenn sie bei einem Verstoß gegen die Security Policy (aka Breach) an die Öffentlichkeit gerieten? Die Antworten auf all diese Fragen, zeigen potenzielle Punkte auf, an denen es anzusetzen gilt, um die gewünschte Sicherheit für das Gesamtsystem zu gewährleisten.

Das wirft wiederum die Frage auf, für welche Punkte die Entwickler der Anwendung und für welche der Cloud-Provider verantwortlich ist. Folgendes Diagramm zeigt anhand des "Shared Responsibility Model for AWS Lambda" die getrennten Zuständigkeiten auf.

Das Shared Responsibility Model zeigt, wer für welchen Bereich verantwortlich ist (Abb. 2).

Für andere Cloud-Provider ergibt sich ein nahezu identisches Bild.

Die folgenden Best Practices für Serverless-Security geben die wichtigsten Ansatzpunkte zur Realisierung einer Security-Policy wieder. Folgende Abbildung zeigt, an welchen Stellen die Best Practices innerhalb einer Serverless-Architektur zum Tragen kommen:

Übersicht über die Best Practices (Abb. 3).

Ein Gesamtbild des Zusammenspiels der in einen Use Case involvierten Komponenten ist essenziell. Intensives Monitoring und Auditing auf Ebene der Serverless Functions hilft, dieses Bild zu erstellen und im Falle von Anomalien zielgerichtet und automatisiert zu reagieren. Anomalien können beispielsweise Aufrufpfade sein, die in der Form nicht vorgesehen sind oder aber unvollständige, unerwartete oder korrumpierte Daten in eingehenden Events.

Sonderheft zu sicherer Softwareentwicklung

Dieser Artikel stammt aus dem iX-Developer-Sonderheft "Sichere Software entwickeln". Es behandelt auf 156 Seiten unter anderem die Themen Web-Application-Security, Codeanalyseverfahren und Cloud-Security. Der Schwerpunkt zu DevSecOps zeigt Methoden, Werkzeuge und Reifegradmodelle auf.

Für spezifische Programmiersprachen soll ein Artikel helfen, Speicherfehler in C++ aufzuspüren und zu vermeiden, und ein weiterer zeigt die Sicherheitskonzepte von Rust auf. Wer mit Java entwickelt, findet eine Übersicht der Änderungen an der Security von Java 11 bis Java 17.

Der Themenbereich Kryptografie geht von den Grundlagen über die Fallstricke beim Einbinden kryptografischer Verfahren in eigene Anwendungen bis zum Ausblick auf die Post-Quanten-Kryptografie.

Das Heft ist im heise Shop als PDF für 12,99 Euro verfügbar. Die gedruckte Ausgabe lässt sich für 14,90 Euro vorbestellen. Ein Bundle aus gedruckter Ausgabe plus PDF ist ebenfalls verfügbar.

Als Best Practice sollten Teams ein intensives und sicheres Logging auf Ebene der Serverless Functions einführen. Log-Informationen sollten dabei aus Gründen der Performance asynchron geschrieben und in einem zentralen Log zur übergreifenden Auswertung gesammelt werden. Zusätzliche Kontextinformationen wie Correlation IDs und Tags helfen beim zielgerichteten Monitoring und erleichtern das Auffinden von Problemen und deren Ursachen. Tools können sowohl die Metriken und Log-Information einzelner Serverless Functions visualisieren als auch deren Zusammenspiel. Über Thresholds lassen sich Alarme auslösen, Benachrichtigungen versenden und automatisiert Gegenmaßnahmen anstoßen.

Ebenso wie in der nicht Serverless-basierten Entwicklung gilt es, die Abhängigkeiten der einzelnen Funktionen der Libraries von Drittanbietern regelmäßig auf Schwachstellen zu prüfen. Die besondere Herausforderung ergibt sich im Umfeld der Serverless-Anwendungen durch die Vielzahl der Funktionen sowie deren Änderungsfrequenz. Die berühmte Dependency Hell erlangt in der Serverless-Welt eine neue Komplexitätsstufe.

Als Best Practice hat es sich etabliert, die benötigten Checks voll automatisiert und als Teil des Continuous-Integration und -Deployment-Lifecycles durchzuführen. Tools zur Sicherstellung der Continuous Code Quality und Code Security wie Snyk, Codacy oder sonarcloud lassen sich nahtlos in die CI/CD-Pipeline integrieren und ermöglichen automatische Security Scans auf unterschiedlichsten Ebenen.

Alternativ bietet das quelloffene Framework Serverless passende Plug-ins.

Cloud-Umgebungen erlauben oft eine feingranulare Einstellung der Zugriffsrechte, die sowohl den Eingang als auch den Ausgang einer Serverless Function steuern. Aufseiten des Eingangs legen die Zugriffsrechte fest, welche anderen Cloud-Komponenten die Funktion mit welcher Art von Payload anstoßen darf. Aufseiten des Ausgangs dagegen bestimmen die Rechte, auf welche anderen Komponenten die Funktion mit welchen Aktionen zugreifen kann.

Dank der feinen Granularität lassen sich Cloud-Komponenten und somit auch Serverless Functions in der Theorie bestmöglich gegen unberechtigte Zugriffe absichern. In der Praxis ist die Vielfalt der Rechte aber häufig äußerst komplex und verwirrend. Daher greifen Teams gerne auf übergreifende Rechte zurück und öffnen damit das Tor für Angriffsszenarien unnötig weit. Der Object Store S3 in AWS – vergleichbar mit einem Dateisystem – unterscheidet etwa 50 Zugriffsberechtigungen für Buckets und Objects. Eine allowAll-Policy in Verbindung mit Wildcards ist daher mehr als verführerisch.

Auch wenn das Auffinden der passenden, minimalen Berechtigung nicht einfach ist und durchaus zu unerwarteten Nebenwirkungen zur Laufzeit führen kann, lohnt sich der Aufwand. Als Best Practice hat sich etabliert, für jede einzelne Funktion eine eigene Rolle mit genau den passenden, minimalen Zugriffsrechten zu definieren. Übergreifende Rollen sollten tabu sein.

Was für die Rollen zutrifft, gilt ebenso für die Rechte innerhalb einer Rolle. Statt beispielsweise in der AWS-Welt einer Rolle ein generelles Lese- und Schreibrecht auf S3 Buckets und Objects zu geben, ist es sinnvoll, diese Rechte explizit getrennt aufzuführen. Ändert sich die Logik der Funktion, lassen sich die Rechte anpassen, ohne das Least Privilege Principle zu brechen.

Für den Zugriff auf Third-Party-Systeme aus der Cloud heraus sollten Teams ebenfalls eigene Rollen mit minimalen Rechten erstellen und den zugreifenden Serverless Functions zuweisen. Bei den meisten Cloud-Providern lassen sich globale und übergreifende Rechte an zentraler Stelle deaktivieren oder durch Tools aufspüren. Davon sollten Teams bei der Cloud-Entwicklung unbedingt Gebrauch machen.

Je breiter die Verantwortung einer Funktion, desto umfangreicher ist das Set der erforderlichen Zugriffsrechte und somit die Gefahr, das Least Privilege Principle zu brechen. Eine einzelne Funktion sollte daher dem Single-Responsibility Pattern folgen und somit die minimal umsetzbare Funktionalität aufweisen. Für das Gesamtsystem ergibt sich dadurch die maximale Granularität.

Dabei gilt es jedoch zu beachten, dass eine äußerst feine Granularität der einzelnen Funktionen zwar die Sicherheit des Systems verbessert, gleichzeitig aber eine erhöhte Komplexität an anderen Stellen mit sich bringen kann. Unter anderem muss gegebenenfalls eine Funktion ihren State an eine andere übergeben. Zudem kann es in einer Kette von Aufrufen zu unnötiger Latenz durch mehrfachen Kaltstart der Serverless Functions kommen. Die Herausforderungen sind jedoch nicht unlösbar.

Best Practice ist, sich zunächst grundsätzlich für die höhere Sicherheit und somit für die feinere Granularität der Funktionen zu entscheiden und erst bei nicht handhabbarer Komplexität Kompromisse einzugehen. Letztere sollten bewusste Entscheidungen sein und vermerkt werden, um zu verhindern, dass der Sicherheitskompromiss erhalten bleibt, wenn er durch Änderung der Funktion obsolet geworden ist.

Eine Serverless Function kann durch Events von diversen Komponenten aus angestoßen werden. Häufig lässt sich innerhalb der Funktion nicht feststellen, wer das Event beziehungsweise die darin enthaltene Payload ursprünglich mit welchem Zweck erzeugt hat. Insbesondere ist nicht sichergestellt, dass die Payload des Events im Vorfeld validiert und auf schädlichen Inhalt geprüft worden ist.

Eine Serverless Function sollte daher grundsätzlich als isolierter Security Context und eingehende Daten als potenziell schädlich und unsicher gelten. Umgekehrt sollten Funktionen ausgehende Daten niemals ungeprüft an andere Cloud-Komponenten weiterleiten.

Es empfiehlt sich, eingehende Events und deren Payload zunächst mit einer Security-Library zu prüfen und gegebenenfalls zu bereinigen. Serverless Functions sollten ausgehende Events und deren Payloads stets neu erzeugen und nicht weiterleiten. Wichtig ist, die Security-Libraries über alle Funktionen hinweg zu verwenden und sie zentral zu verwalten sowie zu aktualisieren.

Die bisher genannten Ansatzpunkte bezogen sich direkt auf die Sicherheit der Serverless-Funktionen. Zusätzliche Ansatzpunkte greifen bereits im Vorfeld.

Dabei gilt als Best Practice, niemals Zugriff auf eine Serverless-Funktion von außen beispielsweise durch Nutzerinteraktion zu ermöglichen. Stattdessen sollten Teams eine zusätzliche Sicherheitsebene wie ein API Gateway oder eine Web Application Firewall (WAF) verwenden. Der Layer ermöglicht das Filtern und Prüfen der eingehenden Daten auf Request-/Response-Mapping-Ebene und erfüllt damit das Fail Fast Principle.

Eine Authentifizierung kann ebenfalls innerhalb des Gateways stattfinden. Da sie nur einmalig pro Aufruf erforderlich ist, bringt sie als positiven Nebeneffekt eine Entlastung der im Verlauf der Use-Case-Abarbeitung aufzurufenden Serverless Functions mit sich.

Sicherheitsaspekte wie DDoS, Traffic Throtteling oder Rate Limiting lassen sich ebenfalls an der Grenze zwischen Außenwelt und Cloud mit einem Security-Gateway elegant lösen. Es vermeidet zudem einen durch Angriffe stark steigenden Datenverkehr und die damit verbundene Kostenexplosion.

Serverless Functions greifen oft lesend oder schreibend auf andere Komponenten zu. Dafür sind die zugehörigen Credentials erforderlich, die sich innerhalb derselben Cloud über das integrierte Identity Access Management abbilden lassen. Gefährlich wird es, wenn Serverless Functions auf die Clouds anderer Provider, auf die Infrastruktur im eigenen Rechenzentrum oder auf Third-Party-Systeme zugreifen. Die dabei übertragenen sensiblen Daten sind für Angreifer Gold wert. Selbst abgelaufene Passwörter können bei der Vorhersage des aktuell gültigen Passworts nützlich sein (Rainbow Table Attacks). Daher ist ein sauber aufgesetztes Secrets Management ein wichtiger Baustein der Serverless-Security-Policy.

Secrets sollten niemals innerhalb des Quellcodes, in Umgebungsvariablen oder im Source-Code-Management-System zu finden sein. Das gilt nicht nur für die Plain-Text-Variante, sondern auch die verschlüsselte Form. Stattdessen sollten Teams eine spezielle Secrets Engine verwenden. Sie ermöglicht sowohl das Verwalten der Secrets inklusive des Abrufs zur Laufzeit als auch das anfängliche Generieren und die automatische Rotation.

Nahezu jeder Cloud-Provider bietet Secrets Engines oder Secrets Stores als integrierten Service an. Wer nach einer plattformunabhängigen Umsetzung sucht, kann auf Tools wie Hashicorp Vault, Akeyless Vault oder Conjur Secrets Manager zurückgreifen.

Da Serverless-Anwendungen stark verteilt sind und auf eine umfangreiche asynchrone Kommunikation über Events setzen, bieten sie besonders viele Angriffsflächen, den Datenaustausch zwischen den einzelnen internen und externen Komponenten einer Anwendung abzugreifen oder zu verfälschen.

Als Best Practice zum Vermeiden von Attacken wie Men in the Middle sollten Anwendungen überall, wo es möglich ist, HTTPS zur Kommunikation verwenden. Darüber hinaus sollten sie SSL-Zertifikate von Remote Identities stets prüfen und im Falle eines Prüffehlers die zugehörige Kommunikation und weitere Verarbeitung abbrechen. Antworten von Third-Party-Services sollten grundsätzlich als unsicher angesehen und entsprechend vor der Verarbeitung geprüft und gegebenenfalls bereinigt oder abgelehnt werden.

Nicht zuletzt gilt für jede Software, dass Unternehmen den Aspekt Security bereits während des Designs und der Entwicklung aktiv mitberücksichtigen sollten. Serverless Functions stellen keine Ausnahme dar. Ganz im Gegenteil: Da Angreifern das Ziel der (virtualisierten) Server fehlt, konzentrieren sich deren Bemühungen in der Welt der Serverless Applications auf die Business-Logik.

Als Best Practice sollten Teams Security Coding Conventions aufbauen, etablieren und (automatisch) prüfen, welche sich an den OWASP-Empfehlungen und insbesondere an den Sicherheitsrisiken der OWASP Top 10 orientieren.

Eine auf Serverless Functions basierende Anwendung zeichnet sich durch Hochverfügbarkeit, nahezu beliebige Skalierung und schnelle Antwortzeiten aus. Kosten fallen nur an, wenn die Applikation tatsächlich Last erzeugt. Der Preis für die Vorteile ist eine stark verteilte, Event-getriebene und asynchron kommunizierende Architektur.

Am Ende steht ein System, das aus Sicht der Security gegenüber einem klassischen Monolithen eine ganze Reihe neuer Angriffspunkte bietet. Best Practices und Patterns wie Security Gateway, Least Privilege Principle, Secrets Store und Isolated Context in Kombination mit Security Coding Conventions helfen, den neuen Herausforderungen zu begegnen.

Entscheidend für den Erfolg im Kampf gegen potenzielle Angriffe ist, dass Teams Sicherheitsmaßnahmen von Anfang an einplanen und im Verlaufe des gesamten Lifecycles – angefangen beim Design über die Implementierung bis hin zur Laufzeit – konsequent verfolgen.

Lars Röwekamp,
Gründer und Geschäftsführer bei der open knowledge GmbH in Oldenburg, beschäftigt sich als „CIO New Technologies“ mit der Analyse und Bewertung neuer Software- und Technologietrends. Ein besonderer Schwerpunkt seiner Arbeit liegt derzeit auf Enterprise und Cloud Computing, Big Data und KI, wobei neben Design- und Architekturfragen insbesondere die Real-Life-Aspekte im Fokus seiner Betrachtung stehen.

(rme)