Azure Functions – Security

8. August 2019

Security ist ein Thema, mit dem sich keiner gerne beschäftigt, aber an dem auch keiner vorbeikommt. Das macht auch vor Azure Functions nicht halt.

Security hat viele Facetten; aus Sicht eines App Services wie einer Function App geht es dabei speziell um Authentifizierung. Dies gilt sowohl für den Zugriff auf die Function App, als auch für den Zugriff durch die Function App auf andere Ressourcen.

Genauer lassen sich folgende Szenarien unterscheiden:

  • Zugriff auf fremde Ressourcen (innerhalb und außerhalb von Azure)
  • Zugriff auf fremde Ressourcen mit Managed Identity
  • Zugriff auf die Function App durch andere Azure Ressourcen
  • Zugriff auf die Function App von außen

Natürlich können sich diese Szenarien auch mischen.

Zugriff auf fremde Ressourcen

Auf “fremde Ressourcen” greift man mit Azure Functions in jedem Fall zu, da immer der Zugriff auf Azure Storage benötigt wird. Daneben sind aber auch Zugriffe auf Datenbanken oder andere Systeme (auch außerhalb von Azure) denkbar.

Azure Function Security

Der notwendige Connection String für Azure Storage wird in der Konfiguration hinterlegt – sprich, in der local.settings.json für die lokale Ausführung und im Azure Portal für den Betrieb in Azure.

Analog kann man auch die notwendigen Adressen, Passwörter, Connection Strings etc. für andere Ressourcen dort ablegen. Diese kann man per Code auslesen; der letzte Beitrag hat zudem gezeigt, wie man auf die Konfiguration deklarativ in Bindings zugreifen kann.

Zugriff auf fremde Ressourcen mit Managed Identity

Passwörter im Klartext in der Konfiguration stehen zu haben ist nicht immer gewünscht – die Konfiguration ist schließlich für jeden einsehbar, der Zugriff auf die Resource Group hat. Aber nur, weil ein Entwickler Zugriff auf die Logs benötigt, heißt das nicht, dass er auch die SAP-Passwörter sehen darf.

Für solche Fälle hat Microsoft Managed Identities vorgesehen, das Azure-Pendant zu einem technischen Account, verbunden mit Azure Key Vault, um Informationen sicher abzulegen.

Azure Function Security

Da Managed Identity und Key Vault nicht das Thema dieser Serie sind, hier nur vereinfacht zusammengefasst:

Konfiguration:

  • Man weist der Function App unter Platform features/Identity eine Managed Identity in Azure Active Directory zu.
  • Man berechtigt die Manage Identity in Key Vault zum Lesen der Informationen.

Code:

  • Man holt sich ein Token zum Zugriff auf den Key Vault
  • Man fragt im Key Vault die benötigte Information ab.

Der Mehraufwand ist überschaubar und rechtfertigt sich durch die sichere Ablage der sicherheitskritischen Konfiguration.

Typischer Stolperstein in der Praxis: Es sind typischerweise die Entwickler, die dies aufsetzen, eventuell im Rahmen von Continuous Deployment. Gerade die haben aber nicht immer die notwendigen Berechtigungen im Azure Active Directory des Unternehmens oder für das Einrichten bzw. den Zugriff auf den Key Vault.

Das folgende Video zeigt dies ganz anschaulich (ab 13:00):

YouTube

Mit dem Laden des Videos akzeptieren Sie die Datenschutzerklärung von YouTube.
Mehr erfahren

Video laden


Sprung zur passenden Stelle: https://youtu.be/N5I59z3qY0A?t=776

Zugriff auf die Function App durch andere Azure Ressourcen

Bei den meisten Triggern meldet sich die Function App selbst, z.B. über einen Connection String, an. Es gibt aber einen Trigger, der eine Tür nach außen aufmacht, durch die prinzipiell jeder auf die Function App zugreifen kann: Der HTTP Trigger. Das lässt sich mit Bordmitteln auch nicht verhindern – jede Function App bekommt eine gültige URL, darauf hat man keinen Einfluss.

Trotzdem lässt sich der Zugriff auf einen HTTP Trigger beschränken: Mit Authorization Keys, die der Aufrufer mitliefern muss.

Das folgende Bild zeigt eine Web App und eine API App, die solche Keys hinterlegt haben und sich damit gegenüber der Function App ausweisen:

Azure Function Security

In diesem Szenario stellen die Web App bzw. API App sicher, dass der ursprüngliche Aufrufer berechtigt ist. Bei ihrem eigenen Aufruf an die Function App übergibt sie dann den hinterlegten Authorization Key. Dieser wird von der Laufzeitumgebung von Azure Functions anhand der Anforderungen der Function geprüft.

Der HTTP-Trigger definiert, welchen Authorization Key er erwartet:

 image

Mögliche Werte sind dabei Anonymous, Function und Admin.
System und User scheinen Relikte ohne Bedeutung zu sein.

Die eigentlichen Keys können dem Azure Portal entnommen werden: In der Function App eine einzelne Function auswählen und dort den Punkt “Manage” selektieren:

image

Es gibt Function Keys, die für die einzelne Function gelten, sowie Host Keys, die für alle Functions der Function App Gültigkeit haben. Von beiden kann man weitere Keys anlegen, z.B. wenn man unterschiedliche Abnehmer hat.

Die Werte der Enumeration bilden sich dabei wie folgt auf die Keys aus dem Portal ab:

  • Anonymous: Es wird kein Key erwartet und auch keiner geprüft.
  • Function: Der Aufrufer muss einen Function Key oder Host Key übergeben.
  • Admin: Der Aufrufer muss den Key “_master” übergeben.
  • User, System: Nicht verwendet.

Da die Herausgabe des Keys _master nicht empfohlen werden kann (dies ist auch der einzige Key, der nicht gelöscht werden kann), bleiben nur Anonymus und Function als sinnvolle Werte.

Der Aufrufer muss diesen Key als Header-Parameter (alternativ als Query-Parameter) übergeben.

Wenn man diesen Weg geht, sollte man beachten, dass es sich bei den Keys um “Shared Secrets” handelt – sie sollten also das Backend nicht verlassen. Im gerade betrachteten Szenario sind die Aufrufer selbst Azure Ressourcen, das ist also sichergestellt.

Übrigens: Falls die Absicherung durch Keys nicht ausreichend ist, bietet die Dokumentation Hinweise, wie man den Zugriff mit Hilfe anderer Azure Dienste darüber hinaus absichern kann.

Zugriff auf die Function App von außen

Bleibt noch der letzte Fall: Die Azure Function App soll einen HTTP Trigger öffentlich verfügbar machen, aber gleichzeitig gegen unberechtigte Nutzung absichern. Typisches Szenario: Ich möchte aus einer Mobile App heraus eine Azure Function aufrufen:

Azure Function Security HTTP Trigger

Zentrale Herausforderung ist hier, dass die Function App lediglich ein übergebenen Authentication Token überprüfen, aber keinen Authentication Workflow initiieren kann. Dies muss die aufrufende Anwendung sicherstellen. Dabei gibt es zwei Szenarien zu unterscheiden:

  • Man nutzt “Authentication/Authorization” im Azure Portal.
  • Der Client authentifiziert sich selbständig und gibt das passende Token weiter

Im ersten Fall konfiguriert man in der Function App einen Authentication Provider:

image

Der eigentliche Client muss sich ebenfalls gegenüber diesem Provider authentifizieren (zum Beispiel Xamarin) und die entsprechenden Informationen – das Token – beim Aufruf an die Function App mitgeben. Azure gibt der Function dann über HTTP Header Informationen über die Authentifizierung mit. Enthalten ist mindestens eine User ID und ein Klartextname, je nach Provider kommen weitere Informationen dazu, etwa das Token. Über die URL /.auth/me können zudem weitere Details, etwa die Emailadresse, abgerufen werden.

Das Video oben geht auch auf diesen Fall ein, wenn auch im Kontext einer Web App. Vorteil dieser Vorgehensweise ist, dass einem Azure eine Menge Arbeit abnimmt. Nachteil ist, dass man im Normalfall immer nur einen Provider angeben kann.
Alternative zur Konfiguration in Azure ist, dass die Function App ein übergebenes Token selbst prüft. Das kann sinnvoll sein, wenn nur einzelne Functions authentifiziert werden sollen oder wenn Provider benötigt werden, die in Azure nicht unterstützt werden.

Zwar muss die Function App das übergebene Token jetzt selbst verifizieren, das ist aber nur ein einfacher Web-Request an den Anbieter. Zudem benötigt man hierfür keine Registrierung beim Anbieter; das Registrieren und Hinterlegen von Client ID/Client Secret entfällt also.

Nachteil dieser Lösung: Da jeder OAuth-Anbieter eigene URLs und Konventionen hat, muss man leider jeden Anbieter gesondert anbinden, auch wenn das an sich nicht sehr kompliziert ist. Bibliotheken, die einem diese Arbeit abnehmen, sind leider dünn gesät bzw. werden nicht mehr gepflegt. Ihr Quelltext kann jedoch die nötigen Informationen für die Aufrufe liefern, zum Beispiel Nemiro.OAuth.

Fazit

Anforderungen nach Authentifizierung sind mit Azure Functions relativ einfach umzusetzen, jedenfalls solange die Function App selbst den Aufruf macht, oder der Aufrufer sich ebenfalls in Azure (oder einer anderen geschützten Umgebung) befindet.

Eines der interessantesten Szenarien – der Einsatz als Backend für eine Mobile App – erfordert jedoch einiges an Handarbeit. Und das, obwohl das sowohl in ASP.NET MVC, als auch in Xamarin eigentlich schon erledigt ist.