Security patterns
- By Michael Howard, Simone Curzi, Heinrich Gantenbein
- 6/26/2023
Secrets management patterns
Secrets are important because they are the keys for accessing services and data. Protecting secrets is therefore essential. You could have the most secure authentication mechanism, but if you do not protect the credentials, then you could be compromised.
Unfortunately, protecting your secrets can be complicated. At a certain point, you need them, and that is exactly when a malicious actor could attack you. Therefore, this is one of those situations where sticking with known secure patterns is most important.
Use managed identities
Intent and motivation
Most applications contain multiple components and require some type of interaction among them. This communication typically involves some form of credentials or sensitive information.
For example, suppose you connect to a database. In that case, you need a connection string specifying the name of the server or of the cluster exposing the database and the credentials to access it. Here is the problem: how can you store these credentials securely?
One obvious answer is to store them in configuration files. Unfortunately, this is not secure, because someone could steal them. Of course, you could encrypt the files, but then you have the problem of protecting the encryption key, and so on.
Alternatively, with an IaaS, you could use Data Protection API to encrypt the configuration file. Unfortunately, this approach has a couple of drawbacks, too. First, DPAPI provides only partial security because users with enough rights access secrets protected by DPAPI. Second, it does not protect the secrets in memory.
If you base your solution on App Services, you could use the application settings. These provide some protection and eliminate the need to store secrets in configuration files. Still, they do not protect the secrets in memory. And again, users who have enough rights can read the secrets—for example from the Azure Portal.
Description
A better way to store secrets is to use managed identities. These are service accounts that are entirely managed by Azure AD.
With managed identities, you have no access to the password, and your application doesn’t either. The platform automatically injects it when you send out a request using the managed identity.
Not all possible callers on Azure support managed identities, nor do all possible callees. For a list of callers and callees that do, see https://azsec.tech/hia. This list is updated continuously, so you’ll want to check it often. For example, Azure Cosmos DB didn’t support managed identities until recently.
Managed identities can be system-assigned or user-assigned. The main difference is that system-assigned managed identities are dedicated to the service, while user-assigned managed identities can be shared with various services. Therefore, if you want to minimize management and have many instances of the same application, you can simply assign the same user-assigned managed identity to all of them.
You can assign managed identities to virtual machines (VMs). This doesn’t mean that all applications hosted by the VM can automatically access resources using the assigned managed identity, however. You must write code to leverage this possibility. This typically involves sending a request to the Azure Instance Metadata Service to obtain a token for the required resource. See https://azsec.tech/dff for details on using managed identities with Azure VMs.
Examples
Suppose your code can use managed identities and needs to access a resource that supports managed identities. In this case, you should not store the credentials anywhere. Instead, it would be best to directly use the managed identity to access said resource.
If your code supports managed identities but not the resource, check whether the resource at least supports managed identities for administrative purposes. If so, you might still be able to use managed identities to retrieve some credentials that grant access to the data from the resource. Therefore, it might be possible to have an initialization phase to gather the credentials from the service with your managed identity via the control plane and then use them for any ensuing calls. For example, Microsoft recommended this approach for accessing Cosmos DB data before it introduced support for managed identities.
Related security principles
Attack surface reduction
Economy of mechanisms
Leveraging existing components
Related patterns
Use a centralized identity provider for authentication
Protect secrets with Azure Key Vault
Protect secrets with Azure Key Vault
Intent and motivation
As discussed with the previous pattern, you need some place to store your secrets. For example, you might need to store a private key associated with a certificate instead of a username and password. A typical example is a bring-your-own-key (BYOK) scenario, where you provide a key to be used in some way, like for encrypting an SQL Database using Transparent Data Encryption (TDE).
You cannot always use managed identities to achieve this. For example, a compute service or called resource might not support managed identities. Or if you have an off-the-shelf application hosted in IaaS, it might not have been modified to support managed identities, and therefore it cannot use them. For example, it sometimes takes Microsoft some time to implement support for managed identities in open source solutions, like Redis Cache, that are incorporated as Azure Services.
So, what to do?
Description
Whatever your situation, if you cannot use managed identities to access a resource directly, the best approach is to use Azure Key Vault—a secure and centralized storage for secrets and encryption keys. Azure Key Vault is more secure than the alternatives because it can be isolated and made inaccessible from the internet, even if your application needs to be exposed using private links. You can rely on various services to secure Azure Key Vault, like Microsoft Defender for Key Vault, which—among its other capabilities—can help identify anomalous interactions. It is also more secure than many alternatives because the Premium SKU provides hardware security module (HSM) capabilities to prevent attackers from stealing private keys.
Leveraging Azure Key Vault is not enough to make your solution secure. For example, as discussed in our coverage of the prefer managed identity principle, if you host code in an environment supporting managed identities, like a VM or an App Service, you should use a managed identity to call Azure Key Vault. If you need a key to access Key Vault, you have the problem of protecting that key, which becomes very difficult. With VMs, you can use DPAPI, but this is not possible with a PaaS solution, because they might be occasionally moved to other servers by the Azure infrastructure, rendering your secrets inaccessible.
Another critical decision you need to make is how many Azure Key Vault instances to use. Microsoft recommends dedicating multiple instances for your application—one for each environment. Our recommendation is to have even more of them, particularly for production environments. If your application has multiple layers, you might dedicate an Azure Key Vault to each of them to ensure that an attacker can get hold of only a few secrets in the event of a compromise. (See Figure 3-3.)
FIGURE 3-3 An example of a system with two Key Vaults: one for the front end and one for the back end.
This approach might seem radical and sometimes unnecessary. After all, having multiple Azure Key Vaults results in a more complex solution, introducing additional management burdens and increasing the possibility of mistakes by expanding the attack surface. So, the approach you decide to adopt depends on the characteristics of your solution.
Be aware that you might achieve significant compartmentalization even without dedicated instances. This is typically achieved by leveraging authorization. Azure Key Vault supports two authorization models: access policies and RBAC. With access policies, you assign access rights to whole categories of objects, like secrets or private keys. This means you cannot discriminate between secrets; you can read all of them or none of them. In contrast, with RBAC, you can assign roles even at a single object level. You can then have a single instance, because the object-level RBAC role assignment provides the granularity needed to determine what anyone can access.
As discussed with our coverage of the use RBAC principle, you should use RBAC because it enables you to verify and manage role assignments centrally. Moreover, this comprehensive visibility enables you to adopt tools to identify potential risks and processes to manage role assignments more or less automatically.
Examples
Azure Key Vault is great for storing secrets to access resources that don’t support managed identities, like Azure Cache for Redis. Azure Key Vault is also effective for storing secrets that you cannot replace with managed identities, like connection strings or private keys. When you store private keys, you should choose the Premium SKU to leverage its HSM capabilities. The Standard SKU is enough for all other scenarios.
Related security principles
Fail secure
Least privilege
Leveraging existing components
Related patterns
Use role-based access control (RBAC)
Use managed identities
Use bring your own key (BYOK)