.NET Core Data Protection
One of the main benefits of building a new .NET project using .NET Core is cross platform deployment, however, IIS will still be a common home for ASP.NET Core web applications.
In .netcore 2.0 MVC applications, a transparent feature that is configured during app Startup is Data Protection. Data Protection provides a cryptographic foundation for things like ASP.NET Identity among many others.
When the Data Protection system is initialized, it applies default settings based on the operational environment. These settings are generally appropriate for apps running on a single machine. – Rick Anderson
The app attempts to detect its operational environment and handle key configuration on its own. (cite)
Default Configuration Logic
- 1) If the app is hosted in Azure Apps, keys are persisted to the %HOME%\ASP.NET\DataProtection-Keys folder. This folder is backed by network storage and is synchronized across all machines hosting the app.
- Keys aren’t protected at rest.
- The DataProtection-Keys folder supplies the key ring to all instances of an app in a single deployment slot.
- Separate deployment slots, such as Staging and Production, don’t share a key ring. When you swap between deployment slots, for example swapping Staging to Production or using A/B testing, any app using Data Protection won’t be able to decrypt stored data using the key ring inside the previous slot. This leads to users being logged out of an app that uses the standard ASP.NET Core cookie authentication, as it uses Data Protection to protect its cookies. If you desire slot-independent key rings, use an external key ring provider, such as Azure Blob Storage, Azure Key Vault, a SQL store, or Redis cache.
- 2) If the user profile is available, keys are persisted to the %LOCALAPPDATA%\ASP.NET\DataProtection-Keys folder. If the operating system is Windows, the keys are encrypted at rest using DPAPI.
- 3) If the app is hosted in IIS, keys are persisted to the HKLM registry in a special registry key that is ACLed only to the worker process account. Keys are encrypted at rest using DPAPI.
- 4) If none of these conditions match, keys aren’t persisted outside of the current process. When the process shuts down, all generated keys are lost.
For IIS, the item we’re interested in here is #3. The default configuration will store the keys in the system registry, that way the keys persist between AppPool restarts and machine restarts. It also lets you share the same key between applications if necessary (via a configuration addition to Startup.cs).
Once you deploy your app and run it under an IIS App Pool, you may find that the Data Protection keys are not being persisted. If you have error logging you’ll see entries like this:
- No XML encryptor configured. Key may be persisted to storage in unencrypted form.
- Neither user profile nor HKLM registry available. Using an ephemeral key repository. Protected data will be unavailable when application exits.
- Using an in-memory repository. Keys will not be persisted to storage.
This means that each time your app pool restarts, new keys will be generated and any encrypted codes or values which have been stored or transmitted will no longer be usable. A basic example of this is a Forgotten Password request using ASP.NET Core Identity. If you request a password reset email, an encrypted URL will be sent in the email for you to click on. If the app pool restarts before you get around to clicking that link, the token will not be able to be decrypted and the reset will fail. This scenario becomes much worse if you’re storing long term encrypted data for later decryption.
This issue stems from a bug in IIS itself which may or may not ever be corrected. In order to work around the issue, it’s necessary for you to edit your App Pool to enable User Profile Loading. Once you set your App Pool to load the user profile for the application pool identity, the application will have permission to read and write to the system registry as intended.
Alternatively, you can configure Data Protection to use a different method of key storage, like a UNC share.
To store keys on a UNC share instead of at the %LOCALAPPDATA% default location, configure the system with PersistKeysToFileSystem: