Das Verteilen von Anwendungslogik auf zahlreiche Serverless Functions in der Cloud ermöglicht eine nahezu beliebige Skalierung der Computing-Arbeitslast bei gleichzeitiger Hochverfügbarkeit und guter Performance. Das Management der Infrastruktur entfällt. Da das resultierende System stark verteilt ist, ergeben sich neue Herausforderungen beim Absichern der Anwendung.
Serverless-Architekturen erlauben den Aufbau komplexer Anwendungssysteme,ohne sich um das Management der Infrastruktur kümmern zu müssen. Aspekte wieVerfügbarkeit, Provisionierung und Skalierung übernimmt der Cloud-Provider. DerFokus der Anwendungsentwicklung liegt auf der Implementierung der Fachlichkeitin Form losgelöster Funktionen. Unter dem Strich ist der Ansatz zudem kosteneffizient– vorausgesetzt das Entwicklungsteam hat bei Architektur und Softwaredesignseine Hausaufgaben gemacht. Dank Managed Runtime Model gehören Maintenance, Updatesund Patches für den gesamten (virtualisierten) Hardware- und Software-Stack derVergangenheit an. Das Team kann seinen Fokus zu 100 Prozent auf die Umsetzungder Businesslogik der Anwendung legen.
Soweit die Theorie. In der Praxis ist es am Ende nicht so trivial, wie es dieCloud-Provider gerne anpreisen. Das Aufspalten einer monolithischenAnwendung 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. Abbildung 1 zeigt beispielhaft ein Serverless-Szenario zum Speichern eines Bildes, inklusive zugehörigerBeschreibung in einem virtuellen Reisetagebuch.
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 da-gegen ergibt ein hoch komplexes 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 ins-besondere 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. Abbildung 2 zeigt anhand des „Shared Responsibility Model for AWS Lambda“ die getrennten Zuständigkeiten auf. 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. Abbildung 3 zeigt, an welchen Stellen die Best Practices innerhalb einer Serverless-Architektur zum Tragen kommen.
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 vor-gesehen sind oder aber unvollständige, unerwartete oder korrumpierte Daten in eingehenden Events.
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 über-greifenden 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 naht-los in die CI/CD-Pipeline integrieren und ermöglichen automatische Security Scans auf unterschiedlichsten Ebenen.
Alternativ bietet das quelloffene Framework Serverless passende Plug-ins (die Links zu den Tools und Ressourcen finden sich unter ix.de/zv83).
Cloud-Umgebungen erlauben oft eine feingranulare Einstellung der Zugriffsrechte, die sowohl den Eingang als auch den Ausgang einer Serverless Function steuern. Auf Seiten des Eingangs legen die Zugriffsrechte fest, welche anderen Cloud-Komponenten die Funktion mit welcher Art von Payload an-stoßen darf. Auf Seiten 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 Auf-wand. Als Best Practice hat sich etabliert, für jede einzelne Funktion eine eigene Rolle mit genau den passenden, mini-malen 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 he-raus 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 umfang-reicher ist das Set der erforderlichen Zugriffsrechte und so-mit 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 je-doch 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 so-wie zu aktualisieren.
Die bisher genannten Ansatzpunkte bezogen sich direkt auf die Sicherheit der Serverless-Funktionen. Zusätzliche Ansatz-punkte 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 steigen-den 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 Rechen-zentrum 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 Hashi corp 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 (siehe „Was lange währt“, auf Seite 8) 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 – an-gefangen beim Design über die Implementierung bis hin zur Laufzeit – konsequent verfolgen.
Quellen
Die Links zum Serverless-Framework und zu weiteren Tools für Serverless-Security finden sich unter ix.de/zv83.
Der Artikel ist Teil des iX Developer Sichere Software entwickeln 2021/22, S. 48-51
Das vollständige Magazin ist hier zu finden.
Serverless-Architekturen erlauben den Aufbau komplexer Anwendungssysteme,ohne sich um das Management der Infrastruktur kümmern zu müssen. Aspekte wieVerfügbarkeit, Provisionierung und Skalierung übernimmt der Cloud-Provider. DerFokus der Anwendungsentwicklung liegt auf der Implementierung der Fachlichkeitin Form losgelöster Funktionen. Unter dem Strich ist der Ansatz zudem kosteneffizient– vorausgesetzt das Entwicklungsteam hat bei Architektur und Softwaredesignseine Hausaufgaben gemacht. Dank Managed Runtime Model gehören Maintenance, Updatesund Patches für den gesamten (virtualisierten) Hardware- und Software-Stack derVergangenheit an. Das Team kann seinen Fokus zu 100 Prozent auf die Umsetzungder Businesslogik der Anwendung legen.
Soweit die Theorie. In der Praxis ist es am Ende nicht so trivial, wie es dieCloud-Provider gerne anpreisen. Das Aufspalten einer monolithischenAnwendung 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. Abbildung 1 zeigt beispielhaft ein Serverless-Szenario zum Speichern eines Bildes, inklusive zugehörigerBeschreibung in einem virtuellen Reisetagebuch.
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 da-gegen ergibt ein hoch komplexes 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 ins-besondere 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. Abbildung 2 zeigt anhand des „Shared Responsibility Model for AWS Lambda“ die getrennten Zuständigkeiten auf. 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. Abbildung 3 zeigt, an welchen Stellen die Best Practices innerhalb einer Serverless-Architektur zum Tragen kommen.
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 vor-gesehen sind oder aber unvollständige, unerwartete oder korrumpierte Daten in eingehenden Events.
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 über-greifenden 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 naht-los in die CI/CD-Pipeline integrieren und ermöglichen automatische Security Scans auf unterschiedlichsten Ebenen.
Alternativ bietet das quelloffene Framework Serverless passende Plug-ins (die Links zu den Tools und Ressourcen finden sich unter ix.de/zv83).
Cloud-Umgebungen erlauben oft eine feingranulare Einstellung der Zugriffsrechte, die sowohl den Eingang als auch den Ausgang einer Serverless Function steuern. Auf Seiten des Eingangs legen die Zugriffsrechte fest, welche anderen Cloud-Komponenten die Funktion mit welcher Art von Payload an-stoßen darf. Auf Seiten 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 Auf-wand. Als Best Practice hat sich etabliert, für jede einzelne Funktion eine eigene Rolle mit genau den passenden, mini-malen 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 he-raus 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 umfang-reicher ist das Set der erforderlichen Zugriffsrechte und so-mit 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 je-doch 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 so-wie zu aktualisieren.
Die bisher genannten Ansatzpunkte bezogen sich direkt auf die Sicherheit der Serverless-Funktionen. Zusätzliche Ansatz-punkte 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 steigen-den 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 Rechen-zentrum 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 Hashi corp 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 (siehe „Was lange währt“, auf Seite 8) 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 – an-gefangen beim Design über die Implementierung bis hin zur Laufzeit – konsequent verfolgen.
Quellen
Die Links zum Serverless-Framework und zu weiteren Tools für Serverless-Security finden sich unter ix.de/zv83.
Der Artikel ist Teil des iX Developer Sichere Software entwickeln 2021/22, S. 48-51
Das vollständige Magazin ist hier zu finden.