Securing Sensitive App Settings in .NET Core Apps using Azure Key Vault

First Published: July 31, 2017

Introduction

I have recently been exploring Dot Net Core and building Web APIs with all the latest Dot Net frills. Having spent the couple of years living in AWS land and not having much exposure to Dot Net Core, there was a lot to learn. One AWS feature I use religiously is Key Management Service (KMS), as it allows me to protect sensitive information without having to worry about encryption methods (leave it to the professionals, I say). I wanted to find out how to secure sensitive app data à la KMS, but using Azure services, if possible. I quickly discovered Azure Key Vault, however the instructions on how to configure it and access Key Vault data programmatically were scattered throughout various blog posts and articles (some of which were out of date), so I decided I would write a more concise, developer-friendly guide.

Steps Required

  • Create A Key Vault
  • Create a Secret
  • Register an App in Azure Active Directory
  • Create an API Key for the App
  • Give App-Specific Permissions to Access Key Vault
  • Configure your Dot Net Core Application

I think it goes without saying that you’ll need an Azure account. If you have not done that, go do it now.

NOTE: When I talk about sensitive data, I mean things like database connection strings, access keys, tokens, API keys and such similar items. Key Vault is not designed for storing large data, as you would in a database. If this is not quite clear right now, the examples below should help clarify what I mean.

ALSO NOTE: I have never found the Azure user interface a particularly pleasant or intuitive beast. I shall try to be as explicit and declarative as I can, but please, if something is not clear in my instructions, do not hesitate to leave a comment.

Create A Key Vault

keyvault-1 Dashboard Services list. You can see ‘Key vaults’ on the left.

First things first, we need to create a Key Vault to store all our precious data in. From your Azure Dashboard, you will have a list of services on the left. If ‘Key vaults’ is not already in your list, click on ‘More services’ and use the filter to find it. Select ‘Key vaults’.

Once you are in Key Vault you will most likely see an empty list, unless you or someone else on your team has previously created a vault. At the top, you’ll see an ‘Add’ button. Click it and a new blade will slide in and present you with a form to fill out to create your new vault. Most of these values should be pretty self-explanatory. The ‘Name’ should obviously be something meaningful that perhaps represents the purpose of the vault, but this is up to you. Pick the ‘Resource Group’ you’d like to use and set the ‘Location’ you want the vault to be stored in.We will configure an ‘Access Policy’ for our application with special access later. Finally, click ‘Create’ to finish the process.

Create a Secret

keyvault-2 Key Vault Overview

Open your newly created vault and you will be presented with an overview. An explanation of the full features of Key Vault is out of the scope of this article, but feel free to consult the documentation. What we would like to do is create a ‘Secret’ that stores and encrypts a JSON representation of an object that we will later create in our app. To do this, click on ‘Secrets’ under ‘Settings’ on the left, or under ‘Assets’ in the Overview panel. Once the ‘Secrets’ panel opens up, click the ‘Add’ button at the top so we can create a new one.

keyvault-3

Change the ‘Upload options’ to ‘Manual’. As a convention, I like to specify the ‘Name’ in the format ‘Type-Application-Purpose’, but again, you can set this to whatever you want. The ‘Value’ will be the JSON object containing the data we will later parse as a POCO object. Here is an example of one:

{
"Version": "1.0.0",
"ObjectType": "datasource",
"SqlServer": "data source={DB_IP_ADDRESS, DB_IP_PORT};initial catalog={DB_NAME};user id={DB_USERNAME};password={DB_PASSWORD};"
}

I like to add a Version and ObjectType, as this can later be used for validation purposes, but essentially, this object can be whatever you want it to be. Configure all the remaining settings as you wish. Activation and expiry dates can be used if you only want the secret to be accessed for a specific period of time. When you are finished, click ‘Create’.

Register an App in Azure Active Directory

We now have data protected by Key Vault, but we are not quite finished yet. We need to give our application (secure) access to this data, first.

In the same way we accessed ‘Key vaults’ from the Azure Dashboard earlier, find ‘Active Directory’ (AD). Once inside AD, select ‘App registrations’ from under the ‘Manage’ panel on the left. This is where we will configure the access and permissions our application will have when accessing Key Vault programmatically.

Similarly to Key Vault, when you select ‘App registrations’ you will be presented with a list of any existing Apps you may have. To register your app, click the ‘New application registration’ button above. The form you will be presented with should be pretty simple to understand. Name it whatever you wish, pick the ‘Application type’ and assign a ‘Sign-on URL’ — this last one is arbitrary and can always be changed later. Lastly, click ‘Create’.

Create an API Key for the App

From the ‘App registrations’ menu, you should see your newly created app listed. Click on it. Copy the ‘Application ID’ that you should be able to see under ‘Essentials’. Then, select ‘Keys’ from the ‘API Access’ section on the right. Creating an API Key

Give the Key a meaningful description that will explain its purpose, then set an expiration setting. Click ‘Save’ and your API Key ‘Value’ will be presented to you. Copy this key value now as when you navigate away it will never be presented again. You can always create a new one, if you forgot to copy it.

Give App-Specific Permissions to Access Key Vault

Return to your Key Vault, and select ‘Access Policies’ under the ‘Settings’ panel on the right. Click the ‘Add New’ button. Click the ‘Select Principal’ option to be presented with a new blade. Enter the Application ID of the app in Azure AD into this field, and select the app when it is presented to you. Click the ‘Select’ button at the bottom to confirm. You can now configure the permissions that you wish to grant the application. I recommend following the Principle of Least Privilege, and only assign the necessary permissions. As it is only Secrets that your app needs access to (and read-only access at that), I would suggest picking ‘Get’ and ‘List’ under the ‘Secret permissions’ option. This is all you need to do, so click ‘OK’ to complete this step.

keyvault-5

Principle of Least Privilege

Configure your Dot Net Core Application

Phew! Final step is here, at last.

Now that we have our administrative tasks completed, we can finally get some code in place so that our app no longer has to worry about keeping sensitive data safe.

To interface with Key Vault programmatically, you will need to add the Microsoft.Extensions.Confugiration.AzureKeyVault NuGet package. Once this is installed, create an appSettings.json file if you don’t already have one and create the following values:

{
"KeyVault": {
"Vault": "THE_NAME_OF_YOUR_VAULT",
"ClientId": "THE_APPLICATION_ID_OF_THE_APP_YOU_CREATED",
"ClientSecret": "THE_KEY_YOU_CREATED_FOR_YOUR_APP_IN_AD"
}
}

Paste in the Application ID and the value for the Key you created earlier in the ClientId and ClientSecret fields, respectively, and enter the name of your Key Vault.

Then, change your Startup.cs file to look a bit like this (you may have other settings, too):

public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile($"appSettings.json", true, true)
.AddEnvironmentVariables();

Configuration = builder.Build();

var keyVault = Configuration.GetSection("KeyVault");
builder.AddAzureKeyVault(
$"https://{keyVault["Vault"]}.vault.azure.net/",
keyVault["ClientId"],
keyVault["ClientSecret"],
new KeyVaultSecretManager());

Configuration = builder.Build();
}

The KeyVaultSecretManager is a class that implements IKeyVaultSecretManager and is used to parse and interpret the Secrets we find in Key Vault. A simple implementation of this class would look like this:

public class KeyVaultSecretManager : IKeyVaultSecretManager
{
public bool Load(SecretItem secret)
{
return true; // You can validate the secret to only return specific data, if you wish.
}

public string GetKey(SecretBundle secret)
{
return \$"KeyVaultObjects:{secret.SecretIdentifier.Name}";
}
}

The Load method is used to validate the tokens as they are loaded into your application. You could perform string comparisons to ensure the name of the secret matches the one your application requires, for example. I simply return true as I want all of them.

The GetKey method is slightly different, however, as this specifies the Key that the value retrieved from Key Vault (our JSON object) will be stored under. The KeyVaultObjects: syntax is a special syntax used to identify the property in our IOptions class that the Key Vault data will be assigned to (more on that shortly).

Let us now create the POCO that will store our Key Vault data, once loaded. This class is actually a representation of our appSettings.json file so we can actually use this to access more than just our Key Vault data. You may even already have one. I have included our Key Vault settings that we specified earlier, for reference. Note the KeyVaultObjects property and the type. This will be populated with our Key Vault data on startup.

public class AppSettingsConfig
{
public Dictionary<string, object> KeyVaultObjects { get; set; }
public KeyVault KeyVault { get; set; }
}

public class KeyVault
{
public string Vault { get; set; }
public string ClientId { get; set; }
public string ClientSecret { get; set; }
}

// This is the POCO for our JSON object stored in Key Vault (which will then be assigned to AppSettingsConfig.KeyVaultObjects.)
public abstract class ResourceObject
{
public string Version { get; set; }
public string ObjectType { get; set; }
public string SqlServer {get; set; }
}

Finally, you can access these values at any point in your application by using the IOptions interface that I mentioned earlier. In the example below, I have created a repository class that needs the database connection string we put in Key Vault earlier.

public class Repository
{
private readonly AppSettingsConfig _configOptions;
private string _connectionString;

public Repository(IOptions<AppSettingsConfig> configOptions)
{
_configOptions = configOptions.Value;
var resourceObject = JsonConvert.DeserializeObject<DataSourceResourceObject>(_configOptions["OUR_VAULT_KEY"].ToString());

_connectionString = resourceObject.SqlServer;
}
}

That’s it! This configuration should enable to you to protect your sensitive information in Key Vault and then provide a Dot Net Core with secure access to that data. If you run into any problems setting this up, please let me know. It is a rather long-winded process, but hopefully you will make it through.

Good luck!