diff --git a/astro/src/content/docs/general/data-protection.md b/astro/src/content/docs/general/data-protection.md new file mode 100644 index 00000000..befb8803 --- /dev/null +++ b/astro/src/content/docs/general/data-protection.md @@ -0,0 +1,95 @@ +--- +title: ASP.NET Core Data Protection +description: Comprehensive guide covering key aspects of ASP.NET Core Data Protection. +date: 2026-03-26T08:20:20+02:00 +sidebar: + label: Data Protection + order: 9 +redirect_from: + - /dataprotection/ +--- + +Any Duende server-side application, like IdentityServer or BFF, is developed and deployed as an ASP.NET Core application. While there are a lot of decisions to make, this also means that your implementation can be built, deployed, hosted, and managed with the same technology you're using for any other ASP.NET applications you have. + +It is important to correctly configure ASP.NET Core Data Protection in your application. + +:::tip +Some of our most common support requests are related to [Data Protection Keys](#data-protection-keys). We strongly encourage you to review the rest of this page before deploying to production. +::: + +## About ASP.NET Core Data Protection + +Duende's SDKs, like IdentityServer and BFF, make extensive use of ASP.NET's [data protection](https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/) feature. It is crucial that you configure data protection correctly when deploying your application in production. + +In local development, ASP.NET automatically creates data protection keys, but in a deployed environment, you will need +to ensure that your data protection keys are stored in a persistent way and shared across all load balanced instances of +your implementation. This means you'll need to choose where to store and how to protect the data +protection keys, as appropriate for your environment. Microsoft has [extensive +documentation on data protection](https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview) +describing how to configure storage and protection of data protection keys. + +A typical implementation should include data protection configuration code, like this: + +```csharp +// Program.cs +builder.Services.AddDataProtection() + // Choose an extension method for key persistence, such as + // PersistKeysToFileSystem, PersistKeysToDbContext, + // PersistKeysToAzureBlobStorage, PersistKeysToAWSSystemsManager, or + // PersistKeysToStackExchangeRedis + .PersistKeysToFoo() + // Choose an extension method for key protection, such as + // ProtectKeysWithCertificate, ProtectKeysWithAzureKeyVault + .ProtectKeysWithBar() + // Explicitly set an application name to prevent issues with + // key isolation. + .SetApplicationName("My.Duende.IdentityServer"); +``` + +:::danger[Ensure data protection keys are persisted] +Always make sure data protection is configured to persist data protection keys to storage, using `.PersistKeysTo...()` +for your storage mechanism. If you lose your data protection keys, all data protected with those keys is no longer be readable. + +Additionally, ensure the storage mechanism itself is durable. For example, if you are using the default file system +based key store, make sure that the configured path is not stored on ephemeral storage. If you are using Redis to store +data protection keys using `PersistKeysToStackExchangeRedis()`, ensure that your Redis service is configured to persist +data to a database backup or append-only file. Otherwise, you will lose all data protection keys when your Redis instance reboots. + +For a more advanced setup, you can create a [key escrow sink](https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/extensibility/key-management?view=aspnetcore-10.0#xmlkeymanager), allowing you to store new data protection keys into +a secure storage (e.g., Azure Key Vault) before the new keys are encrypted. +This enables you to restore existing data protection keys in case they become corrupted or lost. +::: + +## Common Problems + +Common data protection problems occur when data is protected with a key that is not available when the data is later +read. A common symptom is `CryptographicException`s in the application logs. For example, when IdentityServer's automatic key +management fails to read its signing keys due to a data protection failure, it will log an error message +such as `"Error unprotecting key with kid {Signing Key ID}."`, and log the underlying +`System.Security.Cryptography.CryptographicException`, with a message like `"The key {Data Protection Key ID} was not +found in the key ring."` + +Failures to read automatic signing keys are often the first place where a data protection problem manifests, but any of +many places where ASP.NET uses data protection might also throw `CryptographicException`s. + +There are several ways that data protection problems can occur: + +1. In load balanced environments, every instance of a Duende server-side app needs to be configured to share data protection keys. + Without shared data protection keys, each load balanced instance will only be able to read the data that it writes. +2. Data protected data could be generated in a development environment and then accidentally included into the build + output. This is most commonly the case for automatically managed signing keys that are stored on disk. If you are + using automatic signing key management with the default file system based key store, you should exclude the `~/keys` + directory from source control and make sure keys are not included in your builds. Note that if you are using our + Entity Framework based implementation of the operational data stores, then the keys will instead be stored in the + database. +3. Data protection derives keys isolated per application name from the generated key material. If you don't specify a name, + the content root path of the application will be used. In .NET 6.0, Microsoft introduced a breaking change: they changed + how ASP.NET Core sets the content root path, which can cause Data Protection issues. This change was reverted in .NET 7.0, + and Microsoft has [documented a workaround in case your application has to restore the correct application name](https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview#set-the-application-name-setapplicationname). + A better solution is to always specify an explicit application name, but know that changing the application name will + cause all existing data protected with the previous application name to become unreadable. +4. When hosting your web application on Microsoft IIS, [special configuration may be required for data protection](https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/advanced#data-protection). + In most default deployments, IIS falls back to using an ephemeral storage for data protection keys, which means that + new keys are generated every time the application pool restarts. We recommend storing data protection keys in a shared location, + such as a protected file share or database, and configuring IIS to use that location for data protection. + diff --git a/astro/src/content/docs/identityserver/deployment/index.md b/astro/src/content/docs/identityserver/deployment/index.md index 476a005a..5659546c 100644 --- a/astro/src/content/docs/identityserver/deployment/index.md +++ b/astro/src/content/docs/identityserver/deployment/index.md @@ -6,7 +6,6 @@ sidebar: label: Overview order: 10 redirect_from: - - /dataprotection/ - /identityserver/v5/deployment/ - /identityserver/v6/deployment/ - /identityserver/v7/deployment/ @@ -96,52 +95,16 @@ Duende IdentityServer makes extensive use of ASP.NET's [data protection](https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/) feature. It is crucial that you configure data protection correctly before you start using your IdentityServer in production. -In local development, ASP.NET automatically creates data protection keys, but in a deployed environment, you will need -to ensure that your data protection keys are stored in a persistent way and shared across all load balanced instances of -your IdentityServer implementation. This means you'll need to choose where to store and how to protect the data -protection keys, as appropriate for your environment. Microsoft has extensive -documentation [here](https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview) -describing how to configure storage and protection of data protection keys. +The recommended practices for setting up and using ASP.NET Core Data Protection for Duende IdentityServer are the same as for other server-side products, like BFF. See the [general ASP.NET Core Data Protection page](/general/data-protection). -A typical IdentityServer implementation should include data protection configuration code, like this: - -```csharp -// Program.cs -builder.Services.AddDataProtection() - // Choose an extension method for key persistence, such as - // PersistKeysToFileSystem, PersistKeysToDbContext, - // PersistKeysToAzureBlobStorage, PersistKeysToAWSSystemsManager, or - // PersistKeysToStackExchangeRedis - .PersistKeysToFoo() - // Choose an extension method for key protection, such as - // ProtectKeysWithCertificate, ProtectKeysWithAzureKeyVault - .ProtectKeysWithBar() - // Explicitly set an application name to prevent issues with - // key isolation. - .SetApplicationName("IdentityServer"); -``` - -:::danger[Ensure data protection keys are persisted] -Always make sure data protection is configured to persist data protection keys to storage, using `.PersistKeys...()` -for your storage mechanism. - -In addition, make sure the storage mechanism itself is durable. For example, if you are using the default file system -based key store, make sure that the configured path is not stored on ephemeral storage. If you are using Redis to store -data protection keys using `PersistKeysToStackExchangeRedis`, ensure that your Redis service is configured to persist -data to a database backup or append-only file. Otherwise, when your Redis instance reboots, you will lose all data -protection keys. - -If you lose your data protection keys, all data protected with those keys to no longer be readable. -::: - -### Data Protection Keys and IdentityServer's Signing Keys +### ASP.NET Data Protection Keys and IdentityServer Signing Keys ASP.NET's data protection keys are sometimes confused with IdentityServer's signing keys, but the two are completely separate keys with different purposes. IdentityServer implementations need both to function correctly. -#### Data Protection Keys +#### ASP.NET Data Protection Keys -Data protection is a cryptographic library that is part of the ASP.NET framework. Data protection uses private key +Data protection is a cryptographic library that is part of ASP.NET Core. Data protection uses private key cryptography to encrypt and sign sensitive data to ensure that it is only written and read by the application. The framework uses data protection to secure data that is commonly used by IdentityServer implementations, such as authentication cookies and anti-forgery tokens. In addition, IdentityServer itself uses data protection to protect @@ -150,7 +113,7 @@ context objects passed to pages in the UI. The data protection keys are critical implementation because they encrypt a great deal of sensitive data at rest and prevent sensitive data that is round-tripped through the browser from being tampered with. -#### IdentityServer Signing Key +#### The IdentityServer Signing Key Separately, IdentityServer needs cryptographic keys, called [signing keys](/identityserver/fundamentals/key-management.md), to sign tokens such as JWT access tokens and id tokens. The signing keys use public key cryptography to allow client @@ -159,56 +122,7 @@ through [discovery](/identityserver/reference/endpoints/discovery.md). The priva also critical secrets for IdentityServer because a valid signature provides integrity and non-repudiation guarantees that allow client applications and APIs to trust those tokens. -### Common Problems - -Common data protection problems occur when data is protected with a key that is not available when the data is later -read. A common symptom is `CryptographicException`s in the IdentityServer logs. For example, when automatic key -management fails to read its signing keys due to a data protection failure, IdentityServer will log an error message -such as "Error unprotecting key with kid {Signing Key ID}.", and log the underlying -`System.Security.Cryptography.CryptographicException`, with a message like "The key {Data Protection Key ID} was not -found in the key ring." - -Failures to read automatic signing keys are often the first place where a data protection problem manifests, but any of -many places where IdentityServer and ASP.NET use data protection might also throw `CryptographicException`s. - -There are several ways that data protection problems can occur: - -1. In load balanced environments, every instance of IdentityServer needs to be configured to share data protection keys. - Without shared data protection keys, each load balanced instance will only be able to read the data that it writes. -2. Data protected data could be generated in a development environment and then accidentally included into the build - output. This is most commonly the case for automatically managed signing keys that are stored on disk. If you are - using automatic signing key management with the default file system based key store, you should exclude the `~/keys` - directory from source control and make sure keys are not included in your builds. Note that if you are using our - Entity Framework based implementation of the operational data stores, then the keys will instead be stored in the - database. -3. Data protection creates keys isolated by application name. If you don't specify a name, the content root path of the - application will be used. But, beginning in .NET 6.0 Microsoft changed how they handle the path, which can cause data - protection keys to break. Their docs on the problem - are [here](https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview#setapplicationname), - including a work-around where you de-normalize the path. Then, in .NET 7.0, this change was reverted. The solution is - always to specify an explicit application name, and if you have old keys that were generated without an explicit - application name, you need to set your application name to match the default behavior that produced the keys you want - to be able to read. -4. If your IdentityServer is hosted by IIS, special configuration is needed for data protection. In most default - deployments, IIS lacks the permissions required to persist data protection keys, and falls back to using an ephemeral - key generated every time the site starts up. Microsoft's docs on this issue - are [here](https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/advanced?view=aspnetcore-7.0#data-protection). - -### Identity Server's Usage of Data Protection - -Duende IdentityServer's features that rely on data protection include - -* protecting signing keys at rest ( - if [automatic key management](/identityserver/fundamentals/key-management.md#automatic-key-management) is used and enabled) -* protecting [persisted grants](/identityserver/data/operational.md#persisted-grant-service) at rest (if enabled) -* protecting [server-side session](/identityserver/ui/server-side-sessions/index.md) data at rest (if enabled) -* protecting [the state parameter](/identityserver/ui/login/external.md#state-url-length-and-isecuredataformat) for - external OIDC providers (if enabled) -* protecting message payloads sent between pages in the UI ( - e.g. [logout context](/identityserver/ui/logout/logout-context.md) and [error context](/identityserver/ui/error.md)). -* session management (because the ASP.NET Core cookie authentication handler requires it) - -## IdentityServer Data Stores +### IdentityServer Data Stores IdentityServer itself is stateless and does not require server affinity - but there is data that needs to be shared between in multi-instance deployments. @@ -233,6 +147,18 @@ You can either use a traditional database for storing operational data, or use a Duende IdentityServer includes storage implementations for above data using EntityFramework, and you can build your own. See the [data stores](/identityserver/data) section for more information. +### IdentityServer Features Using Data Protection + +Duende IdentityServer's features that rely on data protection include: + +* protecting signing keys at rest (if [automatic key management](/identityserver/fundamentals/key-management.md#automatic-key-management) is used and enabled) +* protecting [persisted grants](/identityserver/data/operational.md#persisted-grant-service) at rest (if enabled) +* protecting [server-side session](/identityserver/ui/server-side-sessions/index.md) data at rest (if enabled) +* protecting [the state parameter](/identityserver/ui/login/external.md#state-url-length-and-isecuredataformat) for + external OIDC providers (if enabled) +* protecting message payloads sent between pages in the UI (e.g. [logout context](/identityserver/ui/logout/logout-context.md) and [error context](/identityserver/ui/error.md)). +* session management (because the ASP.NET Core cookie authentication handler requires it) + ## Distributed Caching Some optional features rely on ASP.NET Core distributed caching: