Skip to content

Commit 065ba19

Browse files
committed
Draft a section on how to use a separate key manager to eliminate races
1 parent 4963274 commit 065ba19

File tree

1 file changed

+51
-0
lines changed
  • aspnetcore/security/data-protection/configuration

1 file changed

+51
-0
lines changed

aspnetcore/security/data-protection/configuration/scaling.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,57 @@ The following scenarios do ***NOT*** provide automatic key storage in a shared l
3131
](/azure/container-apps/dotnet-overview#autoscaling-considerations).
3232
* Distributed apps that don't have a shared storage location or synchronization mechanism for Data Protection keys.
3333

34+
## Managing Data Protection keys outside the app
35+
36+
An app with multiple instances may occasionally see an error like `System.Security.Cryptography.CryptographicException: The key {A6EF5BC2-FDCC-4C0C-A3A5-CDA9A1733D70} was not found in the key ring.`. This can happen when instances get out of sync and data protected on one instance (e.g. an anti-forgery token) is unprotected on another instance (e.g. because a form was served from the former and posted to the latter) that doesn't yet know about that key. When this happens, an app user may have to resubmit a form or re-authenticate (if it was an authentication token that couldn't be unprotected).
37+
38+
One common reason app instances end up with different sets of keys is that, in the absence of a usable key (e.g. due to expiration, lack of access to the backing repository, etc), an instance will generate a new key of its own. Until that key has propagated to all other instances (which can take up to two days), there's a risk that data protected with that new key will sent to an instance that doesn't know how to unprotect it.
39+
40+
Generally, app instances don't know about each other, so coordinating the generation and distribution of new keys (e.g. when they are periodically rotating) requires explicit configuration. One way to avoid having instances generate and use keys that are unknown to other instances is to prevent them from generating keys at all. The details of how to accomplish this vary slightly from app to app, but the general approach is straightforward.
41+
42+
First, app instances [disable key generation](xref:security/data-protection/configuration/overview#disableautomatickeygeneration). Next, a new component is introduced that connects to the same key repository and performs a dummy protect operation once a day or so.
43+
44+
For example, with Azure blob storage as the key repository, the key manager could be a simple console app run on a schedule.
45+
46+
```csharp
47+
using Azure.Identity;
48+
using Microsoft.AspNetCore.DataProtection;
49+
using Microsoft.Extensions.Azure;
50+
using Microsoft.Extensions.Configuration;
51+
using Microsoft.Extensions.DependencyInjection;
52+
using Microsoft.Extensions.Hosting;
53+
54+
var hostBuilder = new HostApplicationBuilder();
55+
56+
hostBuilder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: false);
57+
58+
var blobStorageUri = hostBuilder.Configuration["AzureURIs:BlobStorage"]!;
59+
var keyVaultURI = hostBuilder.Configuration["AzureURIs:KeyVault"]!;
60+
61+
// Use the same persistence and protection mechanisms as your app
62+
hostBuilder.Services
63+
.AddDataProtection()
64+
.PersistKeysToAzureBlobStorage(new Uri(blobStorageUri), new DefaultAzureCredential())
65+
.ProtectKeysWithAzureKeyVault(new Uri(keyVaultURI), new DefaultAzureCredential());
66+
67+
using var host = hostBuilder.Build();
68+
69+
// Perform a dummy operation to force key creation or rotation, if needed
70+
var dataProtector = host.Services.GetDataProtector("Default");
71+
dataProtector.Protect([]);
72+
```
73+
74+
```json
75+
{
76+
"AzureURIs": {
77+
"BlobStorage": "https://<storage-account-name>.blob.core.windows.net/<container-name>/keys.xml",
78+
"KeyVault": "https://<key-vault-name>.vault.azure.net/keys/<key-name>/"
79+
}
80+
}
81+
```
82+
83+
Note that app instances will throw exceptions if they need to perform any `Protect` or `Unprotect` operations before the key manager has run for the first time, so it is preferable to execute it before creating app instances.
84+
3485
:::moniker-end
3586

3687
[!INCLUDE[](~/security/data-protection/configuration/scaling/includes/scaling7.md)]

0 commit comments

Comments
 (0)