I want to preface this post with a small, but important comment: there is absolutely no vulnerability here. If you’re looking for some dirt, you won’t find any, and as you read through this, you’ll hopefully realize what’s going on and maybe learn a little about Azure RBAC policy and asymmetric crypto in general.
I guess the title is a little ‘click-baity’ 🙂
I wrote some C# code that encrypts and decrypts data using Azure Key Vault APIs. It’s pretty simple, you can grab the source from https://github.com/x509cert/AzureKeyVault.
In the screenshot below, an exception is raised calling cryptoClient.Decrypt() on line 60, but to get to this point in the code, cryptoClient.Encrypt() has been called on line 55 and it succeeded.
If you take a look at the RBAC policy for my account, you can see I DO NOT HAVE the ability to Decrypt nor Encrypt:
In short, my account:
- Does not have the Key Encrypt right, but my code can encrypt (not expected)
- Does not have the Key Decrypt right, and my code cannot decrypt (expected).
So what’s going on?
Let’s start at the beginning with some pertinent details, if you know Azure RBAC and Azure Key Vault well, you can probably jump straight to the “Why Is the Key Encrypt Policy not enforced” section.
- Azure Key Vault stores and manages three kinds of items: Keys, Secrets and Certificates.
- Key Vault keys are only asymmetric RSA or Elliptic Curve keys.
- Keys are real keys used for signing/verifying, wrapping/unwrapping and encryption/decryption.
- RSA and Elliptic Curve keys are asymmetric, comprised of a public key and a private key.
- The public key is, well, public!
- Because the public key is public, it does not need to be securely stored.
- The private key is, you guessed it, private.
- It’s imperative that private keys be protected, preferably directly in hardware or encrypted using hardware-backed keys.
- Only RSA keys can encrypt, sign and wrap; Elliptic Curve keys can only sign (this is an algorithm issue and has nothing to do with Key Vault)
- Key Vault provides multiple layers of defense, including network isolation, IP restrictions, authentication and authorization.
- One form of authorization is role-based access control, or RBAC.
- RBAC policies can be defined at the data-plane (to control day-to-day use of the service) and at the control-plane (to control management of the service).
- Key Vault has fine-grained RBAC controls for Keys, Secrets and Certificates at both the data-plane and the control-plane.
Make sure you thoroughly understand the list above before you move on.
Let’s dig into RBAC policy a little. If you look at https://docs.microsoft.com/en-us/azure/role-based-access-control/resource-provider-operations#microsoftkeyvault you will notice there’re two possible Action Types:
‘Action’ includes RBAC policy options at the control-plane (ie; Delete a Key Vault) and ‘DataAction’ includes RBAC policy options at the data-plane (ie; Read a key.)
Here are some examples. First some control-plane policies:
|Action||Microsoft.KeyVault/vaults/read||View the properties of a key vault|
|Action||Microsoft.KeyVault/vaults/write||Create a new key vault or update the properties of an existing key vault|
|Action||Microsoft.KeyVault/vaults/delete||Delete a key vault|
And some data-plane policies:
|Encrypt plaintext with a key. Note that if the key is asymmetric, this operation can be performed by principals with read access.|
|Decrypt ciphertext with a key.|
|Delete a key.|
Why Is the Key Encrypt Policy not enforced?
So let’s get back to the opening question. Why can my code encrypt data when my account does not have the right to do so?
The answer is:
The encryption is not performed by Key Vault,
it’s performed by the Key Vault SDK code.
There is no need to perform the encryption operation in Key Vault because:
Asymmetric encryption uses a public key.
The public key is public! Remember the list at the start of this post, items 5 and 6? In theory, anyone can use the public key to encrypt, but it takes the private key to decrypt.
The decryption operation is performed by Key Vault and hence there is an RBAC policy check. In other words:
We must protect the decrypt operation because it uses a private key.
Remember, today Key Vault keys are always asymmetric. If you’re wondering if Key Vault can store AES keys, it can, but they are stored as secrets, not keys, and because they are secrets, they cannot be used by Key Vault to perform encryption/decryption. You can pull the secret from Key Vault into your code and perform AES crypto in your code. Your code would then be subject to a different bucket of RBAC policies:
If you must block someone from reading a public key from Key Vault, you can remove the Get Key role from their access policy:
On a side note, you can see that only the decrypt operations are performed in Key Vault by looking at the logs using Azure Monitor:
Finally, this situation is described in the description of the Microsoft.KeyVault/vaults
/keys/encrypt/action RBAC policy: “Encrypt plaintext with a key. Note that if the key is asymmetric, this operation can be performed by principals with read access.”
Technically, the Key Encrypt RBAC policy in Key Vault is not needed when using asymmetric encryption because anyone and everyone can access the public key, other Key Vault defenses aside.
If you can read the key, you can encrypt with it and encryption outside of Key Vault is not the purview of Key Vault.
The Key Decrypt RBAC policy is needed to decrypt because the decrypt operation requires access to the private key, and that’s a trusted operation performed by Key Vault.
TL;DR: This is working as intended!
Big thanks to Heath Stewart, Hervey Wilson and Scott Schaab on the Key Vault team for their valuable assistance and feedback.