How to distribute Data Protection keys with an ASP.NET Core web app
… with Redis.
When you configure the Data Protection in your ASP.NET Core web app, by default, it initializes with standard configurations, generally appropriate to a single machine and a single instance web app.
Web applications often need to store security-sensitive data.
The ASP.NET Core data protection stack is designed to serve as the long-term replacement for the <machineKey> element in ASP.NET 1.x — 4.x.
If you see yourself in one of the following scenarios, this “How To” is for you:
- Your app is spread across multiple machines
- You have multiple apps in the same machine with shared data
- For compliance reasons
Many web apps already use Redis as a cache service that can be shared across several servers. Here, I want to show you how to distribute Data Protection keys using the same service - the Redis.
AddDataProtection
The extension method AddDataProtection
, for an IServiceCollection
interface returns an IDataProtectionBuilder
that “exposes” other extension methods to customize the best scenario for your application.
Keep in mind that when you are configuring the services of your application (Authentication, Session, Anti-Forgery) some of them try to add the data protection layer. So, it’s good practice to register the DataProtection before it happens.
SetApplicationName
The application descriptor in the data protection system enables the isolation of applications. By default, the name is the content root path, but for some scenarios listed above the path can be different for different environments. So it is important to always set the application name when you want to share protected payloads among apps.
Unique name of this application within the data protection system.
PersistKeysTo* and ProtectKeysWith*
Before you configure the key storage it is important to explain what PersistKeysTo* and ProtectKeysWith* configurations are. If you already know about it skip to the next chapter.
PersistKeysTo*
The extension methods that start by PersistKeysTo*
for the IDataProtectionBuilder
interface, configures the key storage that will store the valid and outdated keys. To share the keys among apps the key storage should be accessible to every app. e.g. Redis Server, Database Server, Azure Blob, or even a file system. You can see here some key storages available on ASP.NET Core distribution. By default, keys are generated with a 90 days validation period since the activation date. Below you can see an example of a key format stored on a key storage.
After the expiration date, you must store the outdated key to unprotect data that was previously protected by that key. In my experience, the rollout of new keys and the use of outdated ones works very well and ASP.NET Core optimizes the caching process of the keys in memory. See more details here.
ProtectKeysWith*
The keys of your key storage can be protected using a X.509 certificate. It can make sense for the following scenarios:
- The repository may be accessible for many users/applications
- For compliance reasons
ProtectKeys can be configurated using an installed certificate on your machine, or a service that manages your certificates, such as AzureVault or Windows DPAPI.
IMPORTANT: Your certificates are secure and I am assuming that no one has access to them.
PersistKeysToRedis
The Microsoft.AspNetCore.DataProtection.StackExchangeRedis package allows you to store data protection keys in a Redis cache backed by a list entry. This list entry will be stored on the redis key received on the second parameter, e.g. “DataProtection-Keys” as you can see below.
In the Step-by-Step Configuration section, you can see how to configure the XML repository manually, by getting the redis URI and the redis key from the application configurations.
IMPORTANT: Redis is a non-persistent database by default, please ensure that Redis is configured as persistent.
Customize KeyManagementOptions
If you use the configurations using the Microsoft.Extensions.Configuration
with the dependency injection then you can load those configurations and bind the KeyManagementOptions
automatically.
Bellow, you can see two ways to load the options. You can do it by using constants (magic string and number) or by loading the configurations from a configuration section.
Step-by-Step Configuration
- Add the Data Protection dependencies;
- Set application name;
- Configure the key storage (Redis or FileSystem, AzureBlob, EntityFramework, etc);
- Personalize the KeyManagementOptions;
- (Optional) Protect your keys with a certificate X509.
OK, and that’s it! You can check below an example of the entire process and if you want you can see more samples here.
… and as always feel free to contact me if you have any questions.
Conclusion
Data Protection offers a simple consumer-facing API and a simplicity of configurations with almost zero-configuration needed. As we explored, in some cases it is necessary to configure a specific aspect of Redis as key storage. However, it is possible to go deeper and configure data protection with infinite possibilities.
Pros: Using Redis as key storage is the best way to share keys for multiple applications because some web apps already use it to share authentication cookies or CSRF protection across multiple servers.
Cons: If you lose the redis key or the entire database with the master keys of Data Protection, your applications won’t be able to unprotect data previously protected with those keys.
References
- ASP.NET Core Data Protection
- Application Name for Data Protection System
- How to Disable Data Protection in ASP.NET Core
- Configure ASP.NET Core Data Protection
- Key encryption at rest in Windows and Azure using ASP.NET Core
- Key storage format in ASP.NET Core
- Key storage providers in ASP.NET Core — REDIS
- ASP.NET Core 3.1 Data Protection Samples
- Source Code Github DataProtection