Securing ASP.NET Core application settings using Azure Key Vault

Azure Key Vault is one of my favourite services, competing for first place with Azure Functions. And .NET Core is my favorite framework for writing applications. Imagine, then, my suprise when I found out that my favorite tools can now work together!

[Azure Key Vault](https://docs.microsoft.com/en-us/azure/key-vault/key-vault-get-started" target="_blank ) is a cloud service for storing sensitive "secrets" and (encryption) "keys". Key Vault is simple, easy and indispensible when developing secure applications. It helps avoid the complexity of storing sensitive information in configuration files. This can be API passwords, database connections strings etc. You get the point! Previous solutions where convoluted and complex and often got it wrong. A quick search on GitHub can reveal a large number of repos with passwords checked in! Scary.

So how do we avoid this? One answer is to use Azure Key Vault. The team recently added support for KeyVault in .NET Core. Configuration settings and connections strings can now be stored securely outside your application. There's, however, a small caveat; can you see it? Access to KeyVault requires an Internet connection to pull sensitive information. There's a trade off, but it's a small price to pay for the security it provides.

This post will show you how to integrate the KeyVaultProvider with your ASP.NET Core application. The steps required to achieve this, at a high level, are:

  • configure a Service Principal (SP)
  • configure a KeyVault
  • configure .NET Core to use the KeyVaultProvider

1. Setting up a Service Principal (SP)

You'll need to configure a SP in order for your ASP.NET Core application to communicate with Key Vault. Think of the SP as a service account that runs your application and has specific permissions within Azure. For instructions on how to create and configure a Service Principal, have a look at this [post](GHOST_URL/service-principals-in-microsoft-azure/" target="_blank). In this instance, my service principal, named cmtest has only enough permissions to read secrets from Key Vault.

As soon as you set up the Service Principal, make sure you copy the ApplicationId and Application Key somewhere safe before closing down the blade as the Key is not visible afterwards. You'll have to create a new Key etc etc.

Next, we'll setup Key Vault and we'll configure our newly created SP with the appropriate permissions.

2. Setting up the Azure Key Vault

There are many ways for setting up KeyVault. My preferred one (for adhoc things) is to use the [Azure Tools for Visual Studio Code](https://github.com/bradygaster/azure-tools-vscode" target="_blank)! Our new FOSS extension for our other FOSS editor. OSS For The Win! Or you can use: PowerShell, Azure CLI and finally, the Portal. I'll use the Portal this time.

To provision the Key Vault, head to the portal and add a new Key Vault.

Once the Key Vault is operational, add the SP we created in step 1.

You only need to give the SP List and Get permissions for Secrets. DO NOT give it full access. It's not required and it will probably get flagged by your security team as an unnecessary elevation of privilege.

Feel free to add any secrets you may want to now. I've added one: helloworld which we'll use for our demo later.

3. KeyVaultProvider for ASP.NET Core

Thanks to Jon Galloway at NDC London for pointing me to this new provider for .NET Core. This is hot off the press and I don't think it's been documented fully or published yet. The GitHub issue where this was originally discussed can be found here. Naturally, I was very curious to take it for a spin and see how developers can benefit from this feature.

First, we need to add the appropriate NuGet package. Open your project.json or .csproj file and add the following package:

"Microsoft.Extensions.Configuration.AzureKeyVault": "1.0.0"

Next, open your appsettings.json file and add the following properties. You could avoid the settings file and use environmental variable instead. I'll use the easy (see: lazy) way.

 "Vault": "<your KeyVault name",
  "ClientId": "<Your Application ID from step 1> ",
  "ClientSecret": "<Your Application Key from step 1>"

And now, the code. Open Startup.cs and add the KeyVaultProvider in the Startup() method as per the example below:

    var config = builder.Build();
    builder.AddAzureKeyVault(
        $"https://{config["Vault"]}.vault.azure.net/",
        config["ClientId"],
        config["ClientSecret"]);

At this point, the middleware has enough information to go and pull all the KeyVault data. We can immediately start pulling secret values using the Configuration API. Inside the Startup.cs you get access to the Configuration API be default.

 // the name of my secret in my Key Vault 
var test = Configuration["helloworld"];

However, if you need to pull configuration settings stored in KeyVault outside your middleware, then we need to make the Configuration API available through DI. Inside our Startup.cs in the ConfigureServices() method add the following line:

// Add application services.
services.AddSingleton<IConfiguration>(Configuration);

This will allow us to access the Configuration API anywhere in the code. In this instance, I'll use my HomeController as an example of how to do it using the built-in Dependency Injection. In the HomeController add the following code:

private IConfiguration Configuration { get; set; }
public HomeController(IConfiguration configuration)
{
     Configuration = configuration;
}

Now, inside one of our controller methods, we can access KeyVault settings like this:
var test = Configuration["helloworld"];

Running the application and setting a breakpoint proves that our solution works! Job done...

Conclusion

The new KeyVaultProvider for ASP.NET Core provides great integration and allows you to secure your configuration settings with ease. However, there are things that you need to look out for.

Issue #1 The KeyVaultProvider is a brute force approach to pulling Key Vault secrets for your application. It will read every single secret and load it to memory. This is not ideal. To minimise the impact, you can filter secret using a application name. You'll be required to prefix all yoru secrets with said application name and at Load() time, the provider will strip out the application name. The provider has an overload as per the example below:

builder.AddAzureKeyVault(     
        $"https://{config["Vault"]}.vault.azure.net/",     
        config["ClientId"],     
        config["ClientSecret"],     
        "myApplicationName");

Issue #2 The provider depends on .NET Framework 4.5.1 or .NET Standard 1.5 or higher. You application needs to target ASP.NET Core 1.1.0 or higher. Older applications are unable to leverage this feature

Issue #3 This is a configuration settings provider not a fully integrated KeyVault client. If you need a different kind of interaction with your Key Vault, you should use the Key Vault SDK for the language of your choice.

I hope this post gave you a good overview of the new feature and that the steps described here can help you secure your application as well. Feel free to add your comments or ask me anything.


  • Share this post on