Introduction

If you start looking at the Azure Key Vault as a secret storage mechanism, you will hopefully come to the conclusion that it doesn't really solve the secret problem, it moves it somewhere else. Fortunately, you can now beat that issue but let us first cover some basics.

Hardware Security Modules

A Hardware Security Module (HSM) is generally used for a specific purpose: delegation of encryption and not simply the storage of data. There are reasons why this is done and reasons why these devices can be 10s of thousands of pounds to buy:
  • They are super fast, being optimised for a single task
  • They can support transparent key rotation
  • They can handle multiple encryption channels simultaneously
  • They usually include anti-tampering, including small explosives, to render any stolen devices useless to an attacker
What they can't do is prevent someone with access to the server application from asking the HSM to perform an encryption or decryption for an attacker, which wouldn't need the key, just the plain test result although this attack should be much easier to protect from, detect and resolve compared to an offline attack.

Azure Key Vault

So Azure Key Vault is based on HSMs, which sounds great, and supports some of the features above, such as key versioning and rotation and this is no surprise, since they are based on Thales HSMs. But there is a major difference.

If you wanted to use it like an HSM and delegate all encryption, it would probably be much slower than normal since your requests are crossing network infrastructure to reach the HSM - in normal usage, you would co-locate the HSM with the servers that are using it - usually on dedicated, isolated networks, something that is not practical in Cloud infrastructure. Also, if you wanted this usage, then you would be tying up an HSM permanently, something that would cost Microsoft, and therefore you, a significant amount of money so the model would end up like AWS, which charges several thousand per month for its use - something out of reach of most small companies.

They also do not support native symmetrical operations currently, only RSA asymmetrical keys, perhaps because these are more vulnerable to the latest advances in quantum computing, who knows.

The Chicken and Egg

So in a normal HSM application, I do not need strong authentication since I can imply authentication by a separate network. If you can talk to the HSM, then you are on the network, which means you have permission. I could add credentials as well to the application but I wouldn't worry too much about this because an attacker would need to attack the application to find the credentials and get physical network access without detection to abuse the system. Also, as previously stated, most encryption happens inside the HSM in a normal setup so the attacker would not be able to obtain keys, only decrypt chosen ciphertexts at best.

In Azure, you do not have dedicated networks. At best you would get a virtual network between servers and keyvault but I can't see how this would be possible since you don't specify which apps connect to the KeyVault, you simply add code to the application and it finds the KV by URL. In other words you need authentication credentials and they will need to be accessible to the application. Add to this issue the fact that any symmetrical keys would need to be obtained from the Key Vault and used in-memory in the application (i.e. not delegated to the KV) and the Key Vault doesn't seem to add any value. The keys are only stored securely but are then exposed into the application, the very thing that is considered insecure and the justification for using the Key Vault in the first place!

Solving It

In the early days, authentication would use Active Directory and/or direct RBAC style authentication but this doesn't solve the problem. My application could either encrypt these in config using an SSL cert, which is not a rock-solid technique (how easy is it to get the SSL private key if the attacker has already accessed the config file?) or they could use the Application Settings but let's be honest, I think the Azure Portal is much less well-known than the security of my code and much more likely to accidentally give someone access to the portal to see/copy/expose the Key Vault Credentials.

Then they added certificate authentication which uses local SSL certificates and which is probably slightly more secure. The certificate should only be deployed on the App Service (not locally for Developers) and it can have a password - although this is specified in the Azure portal. The password however, is never exposed again to the user and the private key cannot be directly obtained from the portal so this is a better method although I would argue its management on larger systems would not be easy - you would ideally need a separate cert either per application or per application-role, which might quickly get out of hand.

The latest and arguably the best is a hidden and transparent mechanism called Managed Service Identity, which is where Azure assign a unique, hidden identity to each application is enabled on. You can then add permissions for these apps to the Key Vault at different levels and on different keys etc. When an application is running, it automatically assumes that identity (which is rotated regularly and invisibly for security reasons). The great thing is that the code to do this is really straightforward!

The following code is specifically for App Services:

using System.Threading.Tasks;
using Microsoft.Azure.Services.AppAuthentication;
using Microsoft.Azure.KeyVault;
using Microsoft.Azure.KeyVault.Models;

namespace KeyVaultTester
{
public class KeyVault
{
private static readonly KeyVaultClient kvClient;

/// <summary>
/// Static constructor to setup the vault
/// </summary>
static KeyVault()
{
var azureServiceTokenProvider = new AzureServiceTokenProvider();
kvClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
}

/// <summary>
/// Code to access a secret from the key vault
/// </summary>
/// <param name="baseUrl">The baseurl of the keyvault</param>
/// <param name="name">The id of the secret to retrieve</param>
/// <param name="version">The version of the secret to retrieve or blank for the latest version</param>
/// <returns></returns>
public static async Task<SecretBundle> GetTokenForSecret(string baseUrl, string name, string version)
{
return await kvClient.GetSecretAsync(baseUrl, name, version);
}
}
}


Yes! Literally a handful of lines and the system will invisibly obtain its application identity and use it to authenticate with the Key Vault. In this example, I only obtain a secret but there are plenty of other operations. Obviously you should consider Exception that would be thrown due to missing secrets, permission errors etc.