Securing credentials in Azure-hosted applications using managed identities and Azure Key Vault - Implementing Azure Key Vault

In this post, we’ll explore how to strengthen the security of our web applications when accessing credentials for resources hosted outside of Azure.

We are therefore considering a scenario where a web application (such as a Blazor app) is hosted in Azure App Services and needs to access resources hosted outside of Azure (e.g. BigQuery).

Throughout this post, to illustrate our concepts, we will continue working with the Blazor application we developed in the previous posts.

What is the problem ?

In our scenario, the web application needs to access resources that are hosted outside of Azure. Since these external resources—such as BigQuery, which is hosted on GCP—are not managed by Microsoft, they are not registered in Microsoft Entra ID. As a result, we can no longer rely on the managed identities approach described in the previous post.

Does this mean that we cannot securely store credentials in this case ? Absolutely not ! Microsoft provides a solution called Azure Key Vault specifically for this purpose.

What is Azure Key Vault ?

Azure Key Vault is a cloud service offered by Microsoft Azure that provides a secure and centralized way to manage sensitive information such as secrets (e.g. API keys, passwords, connection strings), encryption keys (used for encrypting data) or certificates.

Azure Key Vault keeps secrets and keys in one secure place (centralized storage).

Additionally, we can leverage Azure Role-Based Access Control (RBAC) and Microsoft Entra ID to precisely manage who or what can access the secrets stored in the vault. This means, for example, that a developer can be denied direct access to the vault in a production environment, while still allowing their applications to access it seamlessly.

Important

As the name suggests, Azure Key Vault is hosted on Azure and can therefore be accessed using managed identities. As a result, all the concepts discussed in the previous post remain applicable. In particular, we can configure our Blazor application to be granted access to the vault and retrieve the required credentials securely. Azure Key Vault effectively acts as a secure intermediary between our application and the credentials it needs to access.

To be remembered

Azure Key Vault is therefore particularly suited for storing API keys for SaaS applications or sensitive credentials for other data stores hosted on external cloud platforms.

How can it be used in our Blazor app ?

Creating a new resource in Azure

  • Navigate to the Azure portal and create a new instance of Azure Key Vault.

  • Add a new secret in the vault named saas-api-key for example.

Information

Adding secrets to Azure Key Vault may require assigning the Key Vault Administrator role to gain the necessary permissions to create new secrets (go to Access Control (IAM)).

  • Grant access to the Key Vault using managed identities (with the Key Vault Secrets User role).
Information

This operation simply allows our application to access the vault and its secrets without storing any master passwords or access keys. From this point forward, the web app will be able to retrieve the required credentials without the need to store them in the configuration file.

Modifying the code in Visual Studio

Finally, we need to update the code in Visual Studio to retrieve the necessary credentials. In our case, we are considering a hypothetical scenario where we need to retrieve an API key to connect to a SaaS application.

  • Modify the appsettings.json file to include the URL of the Azure Key Vault.
 1{
 2  "Logging": {
 3    "LogLevel": {
 4      "Default": "Information",
 5      "Microsoft.AspNetCore": "Warning"
 6    }
 7  },
 8  "AllowedHosts": "*",
 9  "StorageConnectionString": "https://eocsmanagedidentity.blob.core.windows.net",
10  "KeyVaultUrl": "https://eocs-managedidentity-kv.vault.azure.net/"
11}

We simply add a new key to the file and specify the URL of the Azure Key Vault that was created earlier.

  • Create a new class named KeyVaultService.cs and add the following code to it.
 1public class KeyVaultService
 2{        
 3    private readonly string _keyVaultUrl;
 4
 5    public KeyVaultService(IConfiguration config)
 6    {            
 7        _keyVaultUrl = config["KeyVaultUrl"];
 8    }
 9
10    public async Task<string> GetStringAsync(string secretName)
11    {
12        SecretClientOptions options = new()
13        {
14            Retry =
15            {
16                Delay= TimeSpan.FromSeconds(2),
17                MaxDelay = TimeSpan.FromSeconds(16),
18                MaxRetries = 5,
19                Mode = RetryMode.Exponential
20             }
21        };
22        var client = new SecretClient(new Uri(_keyVaultUrl), new DefaultAzureCredential(), options);
23
24        var secret = await client.GetSecretAsync(secretName).ConfigureAwait(false);
25
26        return secret.Value.Value;
27    }
28}

This code simply leverages a native C# library to access the Azure Key Vault and retrieve stored secrets.

Information

This code requires the Azure.Identity and Azure.Security.KeyVault.Secrets packages.

  • Register the service (in Program.cs).
1builder.Services.AddSingleton<KeyVaultService>();
  • Update the Counter.razor to retrieve the secrets.
 1@page "/counter"
 2@rendermode InteractiveServer
 3@inject EOCS.ManagedIdentity.Web.Components.Services.BlobService BlobService
 4@inject EOCS.ManagedIdentity.Web.Components.Services.KeyVaultService KeyVaultService
 5
 6<PageTitle>Counter</PageTitle>
 7
 8<h1>Counter</h1>
 9
10<p role="status">Current count: @currentCount</p>
11
12<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
13
14<label>@errorMessage</label>
15
16@code {
17    private int currentCount = 0;
18    private string errorMessage = "";
19
20    private async Task IncrementCount()
21    {
22        currentCount++;
23
24        try
25        {
26            var secretName = "saas-api-key";
27            var v = await KeyVaultService.GetStringAsync(secretName).ConfigureAwait(false);
28            errorMessage = $"The secret key is '{v}'.";
29
30        }
31        catch (Exception ex)
32        {
33            errorMessage = ex.Message;
34        }
35    }
36}

Running the program

All that remains is to deploy this application to Azure and test it (in a real-world application, the retrieved key would be used to connect to a SaaS service and would never be exposed).

Important

In a real-world scenario, of course, it would be entirely inappropriate to disclose a secret in this way. However, the key point lies elsewhere: in production, our application will access the vault via managed identities and retrieve the necessary secrets securely. Additionally, these secrets will remain inaccessible to developers in the production environment. As a result, secrets no longer need to be stored in configuration files, yet they remain accessible to applications when needed.

Final thoughts

If you wish to delve deeper into this topic, acquire the following book, which encompasses all the concepts emphasized in this series and delve into more advanced ones.

Learning Microsoft Azure: Cloud Computing and Development Fundamentals (Andersson))

Do not hesitate to contact me shoud you require further information.