Secrets in ASP.NET Core mit Key Vault und Managed Identity

8 MinutenFelix Burkhard
Wie du unvermeidbare API-Keys aus dem Repository heraushältst, Key Vault als Secret-Quelle einbindest und den Zugriff aus Azure ohne eigenes Client-Secret regelst.

Deine ASP.NET-Core-App soll eine Mail-API aufrufen, deren Anbieter ausschließlich API-Keys akzeptiert. Die Anwendung braucht den Schlüssel zur Laufzeit, im Repository oder im Build-Artefakt hat er aber nichts verloren. Liegt er stattdessen in Azure Key Vault, muss sich die App dort ausweisen, und ein weiteres Passwort für diesen Zugriff würde das Problem nur verschieben.

An diesem Beispiel zeigt der Artikel, wie du den API-Key aus dem Repository heraushältst, Key Vault als Secret-Quelle in ASP.NET Core einbindest und den Zugriff aus Azure per Managed Identity ohne eigenes Client-Secret regelst. Außerdem geht es um Rollen, Netzwerkerreichbarkeit und Rotation, denn ein sauber abgelegtes Secret hilft nur, wenn die App es auch lesen und aktualisieren kann.

Welche Werte in den Key Vault gehören

Ob du überhaupt ein Secret brauchst, hängt vom Zieldienst ab. Unterstützen Dienst und Hosting-Umgebung tokenbasierte Authentifizierung mit Microsoft Entra ID, kann die Anwendung ihre Managed Identity direkt dafür nutzen. Die Mail-API aus dem Beispiel akzeptiert dagegen nur einen API-Key, und dieser Schlüssel bleibt ein Secret, auch wenn er im Key Vault liegt. Der Vault bietet dafür eine zentrale Ablage mit gezielter Zugriffskontrolle.

Nicht jede Einstellung ist ein Secret. Eine Vault-URI, eine API-Basisadresse oder ein Feature-Flag enthält normalerweise keine Anmeldeinformation. Microsoft empfiehlt in den Sicherheitsempfehlungen für Azure Key Vault, den Vault für Schlüssel, Passwörter und andere Secrets zu nutzen und dort nicht die gesamte Anwendungskonfiguration abzulegen. Für allgemeine Einstellungen eignet sich zum Beispiel Azure App Configuration.

Für lokale Entwicklungswerte bieten sich ASP.NET Core User Secrets an. Sie liegen außerhalb des Projektverzeichnisses und landen deshalb nicht im Repository. Da sie nicht verschlüsselt sind, eignen sie sich nur für die Entwicklung. Umgebungsvariablen halten Werte ebenfalls aus dem Quellcode heraus, sind aber häufig Klartext und ersetzen keine Secret-Verwaltung.

Auch ein Secret aus dem Vault liegt nach dem Laden im Prozessspeicher der Anwendung. Logs, Fehlermeldungen, Antworten, Build-Artefakte und Diagnoseausgaben dürfen den Wert deshalb nicht enthalten.

Key Vault als Secret-Quelle in ASP.NET Core

Der Key-Vault-Configuration-Provider stellt Secrets über die gewohnte Konfigurations-API von ASP.NET Core bereit. Dafür brauchst du die NuGet-Pakete Azure.Extensions.AspNetCore.Configuration.Secrets und Azure.Identity. Die Microsoft-Dokumentation zum Key-Vault-Provider beschreibt die Integration und die verfügbaren Optionen.

Den API-Key bindest du an eine eigene Optionsklasse in einer Datei wie MailApiOptions.cs. Über diese Klasse erhält der restliche Anwendungscode den Schlüssel.

public sealed class MailApiOptions
{
    public string ApiKey { get; set; } = string.Empty;
}

In Program.cs liest die Anwendung zuerst die Vault-URI aus einer früheren Konfigurationsquelle und registriert damit den Provider. Anschließend bindet sie die Optionen und prüft beim Start, ob der API-Key gesetzt ist. Als Credential nutzt der Code in Development den AzureCliCredential und auf App Service den ManagedIdentityCredential.

using Azure.Core;
using Azure.Extensions.AspNetCore.Configuration.Secrets;
using Azure.Identity;

var builder = WebApplication.CreateBuilder(args);

var vaultUri = builder.Configuration["KeyVault:Uri"]
    ?? throw new InvalidOperationException("KeyVault:Uri fehlt.");

TokenCredential credential = builder.Environment.IsDevelopment()
    ? new AzureCliCredential()
    : new ManagedIdentityCredential();

builder.Configuration.AddAzureKeyVault(new Uri(vaultUri), credential);

builder.Services.AddOptions<MailApiOptions>()
    .Bind(builder.Configuration.GetSection("MailApi"))
    .Validate(
        options => !string.IsNullOrWhiteSpace(options.ApiKey),
        "MailApi:ApiKey muss gesetzt sein.")
    .ValidateOnStart();

Im Code heißt der Konfigurationsschlüssel MailApi:ApiKey. Im Vault legst du das Secret als MailApi--ApiKey an, weil Secret-Namen keinen Doppelpunkt enthalten dürfen.

Reihenfolge der Konfigurationsquellen

Die Vault-URI ist nicht vertraulich und muss verfügbar sein, bevor der Code den Provider registriert. Weil Key Vault erst nach WebApplication.CreateBuilder hinzukommt, überschreibt der Vault Werte aus früheren Quellen wie Umgebungsvariablen und Kommandozeilenargumenten. Fehlt das Secret im Vault, kann die Konfigurations-API deshalb unbemerkt einen früheren Wert liefern. ValidateOnStart prüft nur den aufgelösten Endwert und erkennt diesen Fallback nicht.

Entferne MailApi:ApiKey deshalb aus appsettings.json, User Secrets, Umgebungsvariablen und Kommandozeilenargumenten. Brauchst du eine andere Priorität, lege sie explizit fest und teste sie.

Key Vault References als Alternative

Läuft die Anwendung ausschließlich auf Azure App Service, kommen statt des Providers auch Key Vault References infrage. App Service löst die Verweise als Anwendungseinstellungen auf, sodass der Anwendungscode keinen Secret-Provider braucht. Die Werte werden zwischengespeichert und innerhalb von 24 Stunden aktualisiert, Änderungen an den Anwendungseinstellungen stoßen eine sofortige Aktualisierung an.

Kann App Service einen Verweis nicht auflösen, kommt unter Umständen die Referenzzeichenfolge selbst als Anwendungseinstellung an. Prüfe deshalb den Auflösungsstatus und nicht nur, ob ein Wert vorhanden ist. Den direkten Provider wählst du, wenn die Anwendung das Laden und die Konfigurationsreihenfolge selbst steuern soll.

Managed Identity als Zugang zum Vault

Mit einer Managed Identity weist sich die Anwendung gegenüber Key Vault aus. Die Managed-Identity-Integration von App Service stellt ihr dafür einen Token-Endpunkt bereit, den Azure Identity abstrahiert. Darüber fordert die Anwendung ein Microsoft-Entra-Zugriffstoken für den Vault an, ohne ein eigenes Passwort oder Client-Secret zu speichern. Welche Secrets sie lesen darf, legen erst die Rollen auf dem Vault fest. Einen breiteren Überblick, wie du Managed Identity auch für SQL, Storage und weitere Dienste einsetzt, findest du in unserem Artikel Managed Identity in Azure: Auth ohne Secrets.

Das Beispiel nutzt eine system-assigned Identity, weil App Service und Identität denselben Lebenszyklus haben. Eine user-assigned Identity passt, wenn sie unabhängig von der App bestehen, vor deren Bereitstellung zugewiesen oder von mehreren Ressourcen genutzt werden soll. Bei geteilter Nutzung erhalten mehrere Workloads dieselben Berechtigungen, was den Wirkungsbereich eines Fehlers vergrößern kann. Die Übersicht zu Managed Identities beschreibt die Unterschiede und Einsatzfälle.

Der Code setzt voraus, dass jede Umgebung außer Development auf App Service mit einer system-assigned Identity läuft. Der Environment-Name belegt allerdings nicht, dass die Plattform diese Identität tatsächlich bereitstellt. Richte sie deshalb in jeder Staging- und Produktionsumgebung ausdrücklich ein. Nutzt du eine user-assigned Identity oder einen anderen Hosting-Anbieter, passe die Credential-Auswahl entsprechend an.

Lokal verwendet AzureCliCredential das Konto, mit dem sich ein Entwickler per az login angemeldet hat, und nicht die Managed Identity der späteren Azure-Ressource. Gib diesem Konto deshalb nur Zugriff auf den Entwicklungs-Vault. DefaultAzureCredential kann die lokale Einrichtung vereinfachen, für produktive Laufzeiten empfiehlt Microsoft jedoch deterministische Credentials. Sonst kann bei Fehlern unbemerkt ein anderes Credential aus der Kette greifen.

Rollen für Runtime und Deployment

Microsoft Entra ID authentifiziert die Anwendung, und Azure Role-Based Access Control (RBAC) legt fest, welche Aktionen diese Identität auf dem Vault ausführen darf. Das Beispiel setzt einen Vault im RBAC-Berechtigungsmodell voraus. Nutzt ein bestehender Vault noch Access Policies, prüfe den Berechtigungsmodus und plane die Migration, bevor du wechselst, denn die Umstellung kann vorhandene Zugriffe ungültig machen.

Der Standard-Provider listet Secret-Namen auf und liest deren Werte. Dafür ist Key Vault Secrets User auf dem Vault-Scope die passende Rolle. Key Vault Reader erlaubt nur den Blick auf Metadaten, nicht auf Secret-Werte. Key Vault Contributor verwaltet die Vault-Ressource, gewährt im RBAC-Modell allein aber keinen Zugriff auf gespeicherte Werte. Die Dokumentation zu Azure RBAC für Key Vault grenzt Verwaltungs- und Datenzugriff voneinander ab.

Mit dieser Rolle kann die Runtime alle Secret-Werte des Vaults lesen, nicht nur MailApi:ApiKey. Ein Vault pro Anwendung und Umgebung begrenzt deshalb, welche Secrets eine Runtime-Identität lesen kann. Bereitstellungen und Rollenvergaben übernimmt eine separate Deployment-Identität oder ein dafür berechtigter Betriebsprozess, damit eine kompromittierte Anwendung ihre eigenen Zugriffsrechte nicht erweitern kann. Plane Rollenänderungen vor dem Rollout ein, weil neue Berechtigungen verzögert wirksam werden können.

Läuft das Deployment in einem Build- oder Continuous-Integration-System (CI) außerhalb von Azure, kannst du für diese Identität Workload Identity Federation prüfen, sofern der externe Identity Provider unterstützt wird. Sie tauscht ein vertrauenswürdiges externes Token gegen ein Microsoft-Entra-Token und vermeidet so ein dauerhaftes Client-Secret. Damit ersetzt sie Zugangsdaten für Microsoft-Entra-geschützte Ressourcen, nicht den API-Key der externen Mail-API. Die Übersicht zu Workload Identity Federation beschreibt unterstützte Szenarien.

Netzwerk, Überwachung und Wiederherstellung

Managed Identity und Rolle klären, wer zugreifen darf, aber nicht, ob die Anwendung den Vault im Netzwerk erreicht. Eine Firewall oder ein Private Endpoint kann den Zugriff trotz korrekter Rolle blockieren. Bei einem privaten Vault müssen deshalb auch die Netzwerkanbindung von App Service und die DNS-Auflösung (Domain Name System) stimmen.

Damit du fehlgeschlagene Zugriffe nachvollziehen kannst, aktiviere Diagnoseprotokolle und Warnmeldungen. Prüfe außerdem, dass Soft Delete und Purge Protection aktiv sind, und teste die Wiederherstellung. Die Sicherheitsempfehlungen für Azure Key Vault behandeln Netzwerk, Isolation und Überwachung gemeinsam.

API-Keys rotieren

Ein neuer Wert im Vault ändert nicht automatisch den API-Key beim Mailanbieter, und die laufende Anwendung übernimmt ihn ebenfalls nicht von selbst. Ein robuster Rotationsablauf beginnt deshalb beim Anbieter. Stelle dort einen neuen Schlüssel aus, speichere ihn im Vault, lass die Anwendung den neuen Wert übernehmen und prüfe einen kontrollierten Aufruf. Widerrufe den alten Schlüssel erst danach. Unterstützt der Anbieter keine überlappende Gültigkeit mehrerer Schlüssel, plane den Wechsel mit einem passenden Wartungsfenster.

Der Key-Vault-Configuration-Provider lädt Secrets standardmäßig beim Start und aktualisiert sie nicht periodisch. Ein konfiguriertes Aktualisierungsintervall oder ein kontrollierter Neustart kann den Wert erneuern, aber ein bestehender Mail-Client muss die neue Option ebenfalls übernehmen. Ein kürzeres Intervall erhöht die Anfragen an Key Vault und sollte zum Aktualisierungsbedarf passen.

Ein Reload der Konfiguration erreicht allerdings nicht automatisch jedes Objekt, das den Schlüssel verwendet. IOptionsMonitor kann geänderte Optionen bereitstellen, ein bereits erstellter Mail-Client muss den neuen API-Key aber ebenfalls verwenden. Prüfe deshalb, wo der Wert gelesen wird und wie lange Objekte oder Verbindungen ihn halten.

Vor dem Rollout klären

Key Vault übernimmt die Ablage des API-Keys, Managed Identity den Zugriff der Anwendung auf den Vault. Den API-Key selbst ersetzt Managed Identity nicht, und Rechte, Netzwerkanbindung und Rotation bleiben Teil des Betriebs. Richte deshalb getrennte Entwicklungs- und Laufzeitidentitäten ein, begrenze ihre Rechte und entscheide bewusst über Provider-Reihenfolge und Aktualisierung.

Teste vor dem Rollout, was bei fehlender Berechtigung, blockiertem Netzwerk und einem abgelaufenen oder rotierten API-Key passiert. Lege fest, wie du Secrets wiederherstellst, und protokolliere Zugriffe, ohne Werte offenzulegen.

Wie viele Zugangsdaten braucht deine Anwendung noch, und welche hast du bereits durch tokenbasierte Authentifizierung ersetzt? Schau gerne bei uns auf LinkedIn vorbei und diskutiere mit.

Autoren