Update: This post shows how to authenticate to azure key vault using app id/secret. However, this approach is less secure than using managed identity for azure resource and certificate for non-azure resource to grant the resource access to the key vault. For production environment, you should definitely consider using azure managed identity or certificate to authenticate and access azure key vault from your resource. Checkout my other post for more details.
In this blog post, I’ll show you the steps on how to keep the credentials out of the source code of an ASP.NET Core app using Azure Key Vault.
If you want some convincing examples why leaving secrets in the source code is bad, check out this post.
I assume you have some familiarity with developing an ASP.NET core 2 app. You also need an Azure subscription to register your application in Azure Active Directory and create an Azure key vault.
Basically the process involves these steps:
Checkout the sample app for this post from my Git repo.
Upon registering the application, you can generate an app id and secret which identify the application. Your app needs to authenticate using the application id and secret when accessing the vault.
Your app needs access to the app id and secret to authenticate with Azure and access the vault.
When setting the variables, use double underscore to separate sections. As per Microsoft’s documentation,
For hierarchical config values specified in environment variables, a colon (
:
) may not work on all platforms. Double underscore (__
) is supported by all platforms.
Other options exist. For instance, you may want to use Secret Manager to store the app credentials under the user account which the app runs. You may also want to Data Protection Apis to encrypt the app id/secret and decrypt them in the app for extra protection.
Follow these steps to create the vault via the portal:
When creating an Azure Key Vault, pay attention to the things below:
[you] will need to consider the trade-off of having the highest security pattern, one where all crypto happens in the Vault boundary. Or, go for reduced operational and latency costs often required for effective bulk data protection, where the Key Vault only provides protection of the ‘secret’ used for bulk data protection (most certainly an AES symmetric key).
For more information on the differences between software vs hardware backed keys, check out this post.
To retrieve the secrets from the vault, your app needs to have Get and List permissions.
In your appsettings.json file, create a section to store the configurations for accessing your key vault. For example,
{ "AllowedHosts": "*", "Vault": { "Dns": "https://sampleappvault.vault.azure.net/", "ClientId": "This would get overriden by environment variables.", "ClientSecret": "This would get overriden by environment variables" } }
The tricky part is naming the variables as described in step 2. However, loading the environment variables is straightforward, using the Microsoft.Extensions.Configuration.EnvironmentVariables package.
public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); builder.AddEnvironmentVariables("SECURE_APP_"); Configuration = builder.Build(); }
One thing to note here is the order of the providers. If two providers define a same configuration key, the ConfigurationBuilder() uses the value from the last provider registered. Since we want to use the ClientId and ClientSecret values from the environment variables, not from the Json file, we need to place the call AddEnvironmentVariables()
after the calls to AddJsonFile()
In the code above, I use the prefix “SECURE_APP_” to selectively load the variables for the app. When not using the prefix, the ConfigurationBuilder() loads all the environment variables, and we don’t want this.
7. Read secrets from Azure Key Vault
This is similar to step 6 above. To retrieve values from the vault, you register the Microsoft provided’s provider.
builder.AddAzureKeyVault(vault: Configuration["Vault:Dns"], clientId: Configuration["Vault:ClientId"], clientSecret: Configuration["Vault:ClientSecret"]); Configuration = builder.Build();
In the above code, value of Vault:Dns comes from the json file. However, values of Vault:ClientId and Vault:ClientSecret are from the environment variables. The environment variables’ names are as follow: SECURE_APP_Vault__ClientId and SECURE_APP_Vault__ClientSecret.
The cost of using Azure Key Vault is insignificant. As of this writing, it’s about $1.03/month for the premium plan which includes HSM backed keys. The process of creating a key vault and reading secrets from the vault is also not that complex, thanks to the abstractions the libraries provide. If you have access to Azure, you should definitely consider securing your secrets using Key Vault. If you can’t use the cloud for some reasons, you may want to consider the Data Protection APIs which I’ll cover in another post.
It’s not worth the risks to store the app settings in the source code.
Tutorial: Configure an Azure web application to read a secret from Key Vault
Get started with Azure Key Vault
Azure Key Vault – Making the cloud safer
Enhancing ASP.NET Core/Blazor App Security and Reusability with HttpMessageHandler and Named HttpClient
Building multitenant application – Part 2: Storing value into database session context from ASP.NET core web API
Building multitenant application – Part 1: Multitenant database using Row Level Security
Azure AD authentication in angular using MSAL angular v2 library
Build and deploy a WebJob alongside web app using azure pipelines
Authenticate against azure ad using certificate in a client credentials flow
Notes on The Clean Architecture
Migrating from Microsoft.AspNetCore.Authentication.AzureAD to Microsoft Identity Web authentication library to integrate with Azure AD.