Configurer la protection des données ASP.NET Core

Lorsque le système de protection des données est initialisé, il applique les paramètres par défaut en fonction de l’environnement opérationnel. Ces paramètres sont appropriés pour les applications s’exécutant sur un seul ordinateur. Toutefois, il existe des cas où un développeur peut vouloir modifier les paramètres par défaut :

  • L’application est répartie sur plusieurs ordinateurs.
  • Pour des raisons de conformité.

Pour ces scénarios, le système de protection des données offre une API de configuration riche.

Avertissement

Comme pour les fichiers de configuration, l’anneau de clés de protection des données doit être protégé à l’aide des autorisations appropriées. Vous pouvez choisir de chiffrer les clés au repos, mais cela n’empêche pas les attaquants de créer de nouvelles clés. Par conséquent, la sécurité de votre application est affectée. L’emplacement de stockage configuré avec Data Protection doit avoir son accès limité à l’application elle-même, comme vous le feriez pour protéger les fichiers de configuration. Par exemple, si vous choisissez de stocker votre anneau de clés sur le disque, utilisez les autorisations du système de fichiers. Vérifiez que seule l’identité sous laquelle votre application web s’exécute dispose d’un accès en lecture, écriture et création à ce répertoire. Si vous utilisez Stockage Blob Azure, seule l’application web doit avoir la possibilité de lire, d’écrire ou de créer de nouvelles entrées dans le magasin d’objets blob, etc.

La méthode d’extension AddDataProtection retourne un IDataProtectionBuilder. IDataProtectionBuilder expose les méthodes d’extension que vous pouvez chaîner ensemble pour configurer les options de protection des données.

Les packages NuGet suivants sont requis pour les extensions de protection des données utilisées dans cet article :

ProtectKeysWithAzureKeyVault

Connectez-vous à Azure à l’aide de l’interface CLI, par exemple :

az login

Pour gérer les clés avec Azure Key Vault, configurez le système avec ProtectKeysWithAzureKeyVault dans Program.cs. blobUriWithSasToken est l’URI complet dans lequel le fichier de clé doit être stocké. L’URI doit contenir le jeton SAS en tant que paramètre de chaîne de requête :

builder.Services.AddDataProtection()
    .PersistKeysToAzureBlobStorage(new Uri("<blobUriWithSasToken>"))
    .ProtectKeysWithAzureKeyVault(new Uri("<keyIdentifier>"), new DefaultAzureCredential());

Pour qu’une application communique et s’autorise avec KeyVault, le package Azure.Identity doit être ajouté.

Définissez l’emplacement de stockage du anneau de clés (par exemple, PersistKeysToAzureBlobStorage). L’emplacement doit être défini, car l’appel ProtectKeysWithAzureKeyVault implémente un IXmlEncryptor qui désactive les paramètres de protection automatique des données, y compris l’emplacement de stockage du anneau de clés. L’exemple précédent utilise Stockage Blob Azure pour conserver le porte-clés. Pour plus d’informations, consultez Fournisseurs de stockage de clés : Stockage Azure. Vous pouvez également conserver le cercle de clés localement avec PersistKeysToFileSystem.

keyIdentifier est l’identificateur de clé du coffre de clés utilisé pour le chiffrement de clé. Par exemple, une clé créée dans le coffre de clés nommée dataprotection dans contosokeyvaulta l’identificateur de clé https://contosokeyvault.vault.azure.net/keys/dataprotection/. Fournissez à l’application les autorisations Get, Unwrap Key et Wrap Key pour le coffre de clés.

ProtectKeysWithAzureKeyVault surcharge :

Si l’application utilise les packages Azure plus anciens (Microsoft.AspNetCore.DataProtection.AzureStorage et Microsoft.AspNetCore.DataProtection.AzureKeyVault), nous vous recommandons de supprimer ces références et de mettre à niveau vers Azure.Extensions.AspNetCore.DataProtection.Blobs et Azure.Extensions.AspNetCore.DataProtection.Keys. Ces packages sont l’endroit où de nouvelles mises à jour sont fournies et résolvent certains problèmes de sécurité et de stabilité clés avec les anciens packages.

builder.Services.AddDataProtection()
    // This blob must already exist before the application is run
    .PersistKeysToAzureBlobStorage("<storageAccountConnectionString", "<containerName>", "<blobName>")
    // Removing this line below for an initial run will ensure the file is created correctly
    .ProtectKeysWithAzureKeyVault(new Uri("<keyIdentifier>"), new DefaultAzureCredential());

PersistKeysToFileSystem

Pour stocker des clés sur un partage UNC au lieu de l’emplacement par défaut %LOCALAPPDATA%, configurez le système avec PersistKeysToFileSystem :

builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"));

Avertissement

Si vous modifiez l’emplacement de persistance des clés, le système ne chiffre plus automatiquement les clés au repos, car il ne sait pas si DPAPI est un mécanisme de chiffrement approprié.

PersistKeysToDbContext

Pour stocker des clés dans une base de données à l’aide d’EntityFramework, configurez le système avec le package Microsoft.AspNetCore.DataProtection.EntityFrameworkCore :

builder.Services.AddDataProtection()
    .PersistKeysToDbContext<SampleDbContext>();

Le code précédent stocke les clés dans la base de données configurée. Le contexte de base de données utilisé doit implémenter IDataProtectionKeyContext. IDataProtectionKeyContext expose la propriété DataProtectionKeys

public DbSet<DataProtectionKey> DataProtectionKeys { get; set; } = null!;

Cette propriété représente la table dans laquelle les clés sont stockées. Créez la table manuellement ou avec DbContext Migrations. Pour plus d’informations, consultez DataProtectionKey.

ProtectKeysWith*

Vous pouvez configurer le système pour protéger les clés au repos en appelant l’une des API de configuration ProtectKeysWith* . Considérez l’exemple ci-dessous, qui stocke les clés sur un partage UNC et chiffre ces clés au repos avec un certificat X.509 spécifique :

builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
    .ProtectKeysWithCertificate(builder.Configuration["CertificateThumbprint"]);

Vous pouvez fournir un X509Certificate2 à ProtectKeysWithCertificate, tel qu’un certificat chargé à partir d’un fichier :

builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
    .ProtectKeysWithCertificate(
        new X509Certificate2("certificate.pfx", builder.Configuration["CertificatePassword"]));

Pour plus d’exemples et une discussion sur les mécanismes de chiffrement de clés intégrés, consultez Chiffrement de clé au repos.

UnprotectKeysWithAnyCertificate

Vous pouvez faire pivoter les certificats et déchiffrer les clés au repos à l’aide d’un tableau de X509Certificate2 certificats avec UnprotectKeysWithAnyCertificate :

builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
    .ProtectKeysWithCertificate(
        new X509Certificate2("certificate.pfx", builder.Configuration["CertificatePassword"]))
    .UnprotectKeysWithAnyCertificate(
        new X509Certificate2("certificate_1.pfx", builder.Configuration["CertificatePassword_1"]),
        new X509Certificate2("certificate_2.pfx", builder.Configuration["CertificatePassword_2"]));

SetDefaultKeyLifetime

Pour configurer le système afin qu’il utilise une durée de vie de clé de 14 jours au lieu des 90 jours par défaut, utilisez SetDefaultKeyLifetime :

builder.Services.AddDataProtection()
    .SetDefaultKeyLifetime(TimeSpan.FromDays(14));

SetApplicationName

Par défaut, le système de protection des données isole les applications les unes des autres en fonction de leurs chemins racine de contenu, même si elles partagent le même référentiel de clés physiques. Cette isolation empêche les applications de comprendre les charges utiles protégées des autres.

Pour partager des charges utiles protégées entre les applications :

builder.Services.AddDataProtection()
    .SetApplicationName("<sharedApplicationName>");

SetApplicationName définit DataProtectionOptions.ApplicationDiscriminatoren interne. À des fins de résolution des problèmes, la valeur attribuée au discriminateur par l’infrastructure peut être journalisée avec le code suivant placé après l’intégration WebApplicationde Program.cs :

var discriminator = app.Services.GetRequiredService<IOptions<DataProtectionOptions>>()
    .Value.ApplicationDiscriminator;
app.Logger.LogInformation("ApplicationDiscriminator: {ApplicationDiscriminator}", discriminator);

Pour plus d’informations sur l’utilisation du discriminateur, consultez les sections suivantes plus loin dans cet article :

Avertissement

Dans .NET 6, WebApplicationBuilder normalise le chemin racine du contenu pour qu'il se termine par un DirectorySeparatorChar. Par exemple, sur Windows, le chemin d’accès racine du contenu se termine par \ et sur Linux /. Les autres hôtes ne normalisent pas le chemin d’accès. La plupart des applications qui migrent à partir de HostBuilder ou WebHostBuilder ne partagent pas le même nom d’application, car elles n’auront pas le nom de fin DirectorySeparatorChar. Pour contourner ce problème, supprimez le caractère de séparateur de répertoire et définissez manuellement le nom de l’application, comme indiqué dans le code suivant :

using Microsoft.AspNetCore.DataProtection;
using System.Reflection;

var builder = WebApplication.CreateBuilder(args);
var trimmedContentRootPath = builder.Environment.ContentRootPath.TrimEnd(Path.DirectorySeparatorChar);
builder.Services.AddDataProtection()
 .SetApplicationName(trimmedContentRootPath);
var app = builder.Build();

app.MapGet("/", () => Assembly.GetEntryAssembly()!.GetName().Name);

app.Run();

DisableAutomaticKeyGeneration

Vous pouvez avoir un scénario dans lequel vous ne souhaitez pas qu’une application restaure automatiquement les clés (créer de nouvelles clés) à mesure qu’elles approchent de l’expiration. Un exemple de ce scénario peut être les applications configurées dans une relation primaire/secondaire, où seule l’application principale est responsable des problèmes de gestion des clés et où les applications secondaires ont simplement une vue en lecture seule de l’anneau de clés. Les applications secondaires peuvent être configurées pour traiter l’anneau de clés en lecture seule en configurant le système avec DisableAutomaticKeyGeneration :

builder.Services.AddDataProtection()
    .DisableAutomaticKeyGeneration();

Isolation par application

Lorsque le système de protection des données est fourni par un hôte ASP.NET Core, il isole automatiquement les applications les unes des autres, même si ces applications s’exécutent sous le même compte de processus de travail et utilisent le même master matériel de clé. Ceci est similaire au modificateur IsolateApps de l’élément <machineKey> System.Web.

Le mécanisme d’isolation fonctionne en considérant chaque application sur l’ordinateur local comme un locataire unique. Par conséquent, le IDataProtector rooté d’une application donnée inclut automatiquement l’ID d’application comme un discriminateur (ApplicationDiscriminator). L’ID unique de l’application est le chemin physique de l’application :

  • Pour les applications hébergées dans IIS, l’ID unique est le chemin physique IIS de l’application. Si une application est déployée dans un environnement de batterie de serveurs web, cette valeur est stable en supposant que les environnements IIS sont configurés de la même manière sur toutes les machines de la batterie de serveurs web.
  • Pour les applications auto-hébergées s’exécutant sur le Kestrel serveur, l’ID unique est le chemin physique de l’application sur le disque.

L’identificateur unique est conçu pour survivre aux réinitialisations, à la fois de l’application individuelle et de la machine elle-même.

Ce mécanisme d’isolation suppose que les applications ne sont pas malveillantes. Une application malveillante peut toujours avoir un impact sur toute autre application s’exécutant sous le même compte de processus de travail. Dans un environnement d’hébergement partagé où les applications ne sont pas approuvées mutuellement, le fournisseur d’hébergement doit prendre des mesures pour garantir l’isolation au niveau du système d’exploitation entre les applications, y compris la séparation des dépôts de clés sous-jacents des applications.

Si le système de protection des données n’est pas fourni par un hôte ASP.NET Core (par exemple, si vous l’instanciez via le type concret DataProtectionProvider), l’isolation de l’application est désactivée par défaut. Lorsque l’isolation d’application est désactivée, toutes les applications soutenues par le même matériel de clé peuvent partager des charges utiles tant qu’elles fournissent les objectifs appropriés. Pour fournir une isolation d’application dans cet environnement, appelez la méthode SetApplicationName sur l’objet de configuration et fournissez un nom unique pour chaque application.

Protection des données et isolation des applications

Tenez compte des points suivants pour l’isolation des applications :

  • Lorsque plusieurs applications sont pointées vers le même référentiel de clés, l’intention est que les applications partagent le même master matériel clé. La protection des données est développée en supposant que toutes les applications partageant un anneau de clés peuvent accéder à tous les éléments de cet anneau de clés. L’identificateur unique de l’application est utilisé pour isoler les clés spécifiques à l’application dérivées des clés fournies par l’anneau de clés. Il ne s’attend pas à ce que les autorisations de niveau élément, telles que celles fournies par Azure KeyVault, soient utilisées pour appliquer une isolation supplémentaire. La tentative d’autorisations au niveau de l’élément génère des erreurs d’application. Si vous ne souhaitez pas vous fier à l’isolation intégrée de l’application, des emplacements de magasin de clés distincts doivent être utilisés et non partagés entre les applications.

  • Le discriminateur d’application (ApplicationDiscriminator) permet à différentes applications de partager le même master matériel clé, mais de conserver leurs charges utiles de chiffrement distinctes les unes des autres. Pour que les applications puissent lire les charges utiles de chiffrement de l’autre, elles doivent avoir le même discriminateur d’application, qui peut être défini en appelant SetApplicationName.

  • Si une application est compromise (par exemple, par une attaque RCE), tous les master matériel clé accessibles à cette application doivent également être considérés comme compromis, quel que soit son état de protection au repos. Cela implique que si deux applications sont pointées vers le même dépôt, même si elles utilisent des discriminateurs d’application différents, une compromission de l’une d’elles équivaut fonctionnellement à une compromission des deux.

    Cette clause « fonctionnellement équivalente à un compromis des deux » est valable même si les deux applications utilisent des mécanismes différents pour la protection des clés au repos. En règle générale, il ne s’agit pas d’une configuration attendue. Le mécanisme de protection au repos est destiné à fournir une protection dans le cas où un adversaire obtient un accès en lecture au dépôt. Un adversaire qui obtient un accès en écriture au dépôt (peut-être parce qu’il a obtenu l’autorisation d’exécution de code au sein d’une application) peut insérer des clés malveillantes dans le stockage. Le système de protection des données ne fournit intentionnellement pas de protection contre un adversaire qui obtient un accès en écriture au dépôt de clés.

  • Si les applications doivent rester vraiment isolées les unes des autres, elles doivent utiliser différents référentiels de clés. Cela s’inscrit naturellement dans la définition de « isolé ». Les applications ne sont pas isolées si elles disposent toutes d’un accès en lecture et en écriture aux magasins de données des autres.

Modification d’algorithmes avec UseCryptographicAlgorithms

La pile de protection des données vous permet de modifier l’algorithme par défaut utilisé par les clés nouvellement générées. Le moyen le plus simple d’effectuer cette opération consiste à appeler UseCryptographicAlgorithms à partir du rappel de configuration :

builder.Services.AddDataProtection()
    .UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration
    {
        EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
        ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
    });

Le EncryptionAlgorithm par défaut est AES-256-CBC, et l’élément ValidationAlgorithm par défaut est HMACSHA256. La stratégie par défaut peut être définie par un administrateur système via une stratégie à l’échelle de l’ordinateur, mais un appel explicite à UseCryptographicAlgorithms remplace la stratégie par défaut.

L’appel UseCryptographicAlgorithms vous permet de spécifier l’algorithme souhaité à partir d’une liste prédéfinie intégrée. Vous n’avez pas à vous soucier de l’implémentation de l’algorithme. Dans le scénario ci-dessus, le système de protection des données tente d’utiliser l’implémentation CNG d’AES en cas d’exécution sur Windows. Sinon, elle revient à la classe managée System.Security.Cryptography.Aes.

Vous pouvez spécifier manuellement une implémentation via un appel à UseCustomCryptographicAlgorithms.

Conseil

La modification des algorithmes n’affecte pas les clés existantes dans l’anneau de clés. Elle affecte uniquement les clés nouvellement générées.

Spécification d’algorithmes managés personnalisés

Pour spécifier des algorithmes managés personnalisés, créez une instance ManagedAuthenticatedEncryptorConfiguration qui pointe vers les types d’implémentation :

builder.Services.AddDataProtection()
    .UseCustomCryptographicAlgorithms(new ManagedAuthenticatedEncryptorConfiguration
    {
        // A type that subclasses SymmetricAlgorithm
        EncryptionAlgorithmType = typeof(Aes),

        // Specified in bits
        EncryptionAlgorithmKeySize = 256,

        // A type that subclasses KeyedHashAlgorithm
        ValidationAlgorithmType = typeof(HMACSHA256)
    });

En règle générale, les propriétés *Type doivent pointer vers des implémentations concrètes et instanciables (via un ctor public sans paramètre) de SymmetricAlgorithm et KeyedHashAlgorithm, bien que les cas spéciaux du système, certaines valeurs comme typeof(Aes) pour des raisons pratiques.

Notes

SymmetricAlgorithm doit avoir une longueur de clé de ≥ 128 bits et une taille de bloc de ≥ 64 bits, et il doit prendre en charge le chiffrement en mode CBC avec remplissage PKCS #7. KeyedHashAlgorithm doit avoir une taille de synthèse de >= 128 bits, et il doit prendre en charge des clés de longueur égale à la longueur de synthèse de l’algorithme de hachage. KeyedHashAlgorithm n’est pas strictement requis pour être HMAC.

Spécification d’algorithmes CNG Windows personnalisés

Pour spécifier un algorithme CNG Windows personnalisé à l’aide du chiffrement en mode CBC avec validation HMAC, créez une instance CngCbcAuthenticatedEncryptorConfiguration qui contient les informations algorithmiques :

builder.Services.AddDataProtection()
    .UseCustomCryptographicAlgorithms(new CngCbcAuthenticatedEncryptorConfiguration
    {
        // Passed to BCryptOpenAlgorithmProvider
        EncryptionAlgorithm = "AES",
        EncryptionAlgorithmProvider = null,

        // Specified in bits
        EncryptionAlgorithmKeySize = 256,

        // Passed to BCryptOpenAlgorithmProvider
        HashAlgorithm = "SHA256",
        HashAlgorithmProvider = null
    });

Notes

L’algorithme de chiffrement par blocs symétriques doit avoir une longueur de clé de >= 128 bits, une taille de bloc de >= 64 bits, et il doit prendre en charge le chiffrement en mode CBC avec remplissage PKCS #7. L’algorithme de hachage doit avoir une taille de synthèse de >= 128 bits et doit prendre en charge l’ouverture avec l’indicateur BCRYPT_ALG_HANDLE_HMAC_FLAG. Les propriétés du fournisseur peuvent être définies sur null pour utiliser le fournisseur par défaut pour l’algorithme spécifié. Pour plus d'informations, voir la documentation de BCryptOpenAlgorithmProvider.

Pour spécifier un algorithme CNG Windows personnalisé à l’aide du chiffrement Galois/Counter Mode avec validation, créez une instance CngGcmAuthenticatedEncryptorConfiguration qui contient les informations algorithmiques :

builder.Services.AddDataProtection()
    .UseCustomCryptographicAlgorithms(new CngGcmAuthenticatedEncryptorConfiguration
    {
        // Passed to BCryptOpenAlgorithmProvider
        EncryptionAlgorithm = "AES",
        EncryptionAlgorithmProvider = null,

        // Specified in bits
        EncryptionAlgorithmKeySize = 256
    });

Notes

L’algorithme de chiffrement par blocs symétriques doit avoir une longueur de clé de >= 128 bits, une taille de bloc d’exactement 128 bits, et il doit prendre en charge le chiffrement GCM. Vous pouvez définir la propriété sur EncryptionAlgorithmProvider null pour utiliser le fournisseur par défaut pour l’algorithme spécifié. Pour plus d'informations, voir la documentation de BCryptOpenAlgorithmProvider.

Spécification d’autres algorithmes personnalisés

Bien qu’il ne soit pas exposé en tant qu’API de première classe, le système de protection des données est suffisamment extensible pour permettre de spécifier presque n’importe quel type d’algorithme. Par exemple, il est possible de conserver toutes les clés contenues dans un module de sécurité matérielle (HSM) et de fournir une implémentation personnalisée des principales routines de chiffrement et de déchiffrement. Pour plus d’informations, consultez IAuthenticatedEncryptordansExtensibilité du chiffrement cœur.

Conservation des clés lors de l’hébergement dans un conteneur Docker

Lors de l’hébergement dans un conteneur Docker, les clés doivent être conservées dans :

  • Un Dossier qui est un volume Docker qui persiste au-delà de la durée de vie du conteneur, tel qu’un volume partagé ou un volume monté sur un hôte.
  • Un fournisseur externe, tel que Stockage Blob Azure (illustré dans la section ProtectKeysWithAzureKeyVault) ou Redis.

Conservation des clés avec Redis

Seules les versions de Redis prenant en charge la persistance des données Redis doivent être utilisées pour stocker des clés. Le stockage Blob Azure est persistant et peut être utilisé pour stocker des clés. Pour plus d’informations, consultez ce problème GitHub.

Journalisation de DataProtection

Activez Information la journalisation de niveau de DataProtection pour faciliter le diagnostic du problème. Le fichier suivant appsettings.json active la journalisation des informations de l’API DataProtection :

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "Microsoft.AspNetCore.DataProtection": "Information"
    }
  },
  "AllowedHosts": "*"
}

Pour plus d’informations sur la journalisation, consultez Journalisation dans .NET Core et ASP.NET Core.

Ressources supplémentaires

Lorsque le système de protection des données est initialisé, il applique les paramètres par défaut en fonction de l’environnement opérationnel. Ces paramètres sont appropriés pour les applications s’exécutant sur un seul ordinateur. Toutefois, il existe des cas où un développeur peut vouloir modifier les paramètres par défaut :

  • L’application est répartie sur plusieurs ordinateurs.
  • Pour des raisons de conformité.

Pour ces scénarios, le système de protection des données offre une API de configuration riche.

Avertissement

Comme pour les fichiers de configuration, l’anneau de clés de protection des données doit être protégé à l’aide des autorisations appropriées. Vous pouvez choisir de chiffrer les clés au repos, mais cela n’empêche pas les attaquants de créer de nouvelles clés. Par conséquent, la sécurité de votre application est affectée. L’emplacement de stockage configuré avec Data Protection doit avoir son accès limité à l’application elle-même, comme vous le feriez pour protéger les fichiers de configuration. Par exemple, si vous choisissez de stocker votre anneau de clés sur le disque, utilisez les autorisations du système de fichiers. Vérifiez que seule l’identité sous laquelle votre application web s’exécute dispose d’un accès en lecture, écriture et création à ce répertoire. Si vous utilisez Stockage Blob Azure, seule l’application web doit avoir la possibilité de lire, d’écrire ou de créer de nouvelles entrées dans le magasin d’objets blob, etc.

La méthode d’extension AddDataProtection retourne un IDataProtectionBuilder. IDataProtectionBuilder expose les méthodes d’extension que vous pouvez chaîner ensemble pour configurer les options de protection des données.

Les packages NuGet suivants sont requis pour les extensions de protection des données utilisées dans cet article :

ProtectKeysWithAzureKeyVault

Connectez-vous à Azure à l’aide de l’interface CLI, par exemple :

az login

Pour stocker des clés dans Azure Key Vault, configurez le système avec ProtectKeysWithAzureKeyVault dans la classe Startup. blobUriWithSasToken est l’URI complet dans lequel le fichier de clé doit être stocké. L’URI doit contenir le jeton SAS en tant que paramètre de chaîne de requête :

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToAzureBlobStorage(new Uri("<blobUriWithSasToken>"))
        .ProtectKeysWithAzureKeyVault(new Uri("<keyIdentifier>"), new DefaultAzureCredential());
}

Pour qu’une application communique et s’autorise avec KeyVault, le package Azure.Identity doit être ajouté.

Définissez l’emplacement de stockage du anneau de clés (par exemple, PersistKeysToAzureBlobStorage). L’emplacement doit être défini, car l’appel ProtectKeysWithAzureKeyVault implémente un IXmlEncryptor qui désactive les paramètres de protection automatique des données, y compris l’emplacement de stockage du anneau de clés. L’exemple précédent utilise Stockage Blob Azure pour conserver le porte-clés. Pour plus d’informations, consultez Fournisseurs de stockage de clés : Stockage Azure. Vous pouvez également conserver le cercle de clés localement avec PersistKeysToFileSystem.

keyIdentifier est l’identificateur de clé du coffre de clés utilisé pour le chiffrement de clé. Par exemple, une clé créée dans le coffre de clés nommée dataprotection dans contosokeyvaulta l’identificateur de clé https://contosokeyvault.vault.azure.net/keys/dataprotection/. Fournissez à l’application les autorisations Get, Unwrap Key et Wrap Key pour le coffre de clés.

ProtectKeysWithAzureKeyVault surcharge :

Si l’application utilise les packages Azure plus anciens (Microsoft.AspNetCore.DataProtection.AzureStorage et Microsoft.AspNetCore.DataProtection.AzureKeyVault), nous vous recommandons de supprimer ces références et de mettre à niveau vers Azure.Extensions.AspNetCore.DataProtection.Blobs et Azure.Extensions.AspNetCore.DataProtection.Keys. Ces packages sont l’endroit où de nouvelles mises à jour sont fournies et résolvent certains problèmes de sécurité et de stabilité clés avec les anciens packages.

services.AddDataProtection()
    //This blob must already exist before the application is run
    .PersistKeysToAzureBlobStorage("<storage account connection string", "<key store container name>", "<key store blob name>")
    //Removing this line below for an initial run will ensure the file is created correctly
    .ProtectKeysWithAzureKeyVault(new Uri("<keyIdentifier>"), new DefaultAzureCredential());

PersistKeysToFileSystem

Pour stocker des clés sur un partage UNC au lieu de l’emplacement par défaut %LOCALAPPDATA%, configurez le système avec PersistKeysToFileSystem :

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"));
}

Avertissement

Si vous modifiez l’emplacement de persistance des clés, le système ne chiffre plus automatiquement les clés au repos, car il ne sait pas si DPAPI est un mécanisme de chiffrement approprié.

PersistKeysToDbContext

Pour stocker des clés dans une base de données à l’aide d’EntityFramework, configurez le système avec le package Microsoft.AspNetCore.DataProtection.EntityFrameworkCore :

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToDbContext<DbContext>()
}

Le code précédent stocke les clés dans la base de données configurée. Le contexte de base de données utilisé doit implémenter IDataProtectionKeyContext. IDataProtectionKeyContext expose la propriété DataProtectionKeys

public DbSet<DataProtectionKey> DataProtectionKeys { get; set; }

Cette propriété représente la table dans laquelle les clés sont stockées. Créez la table manuellement ou avec DbContext Migrations. Pour plus d’informations, consultez DataProtectionKey.

ProtectKeysWith*

Vous pouvez configurer le système pour protéger les clés au repos en appelant l’une des API de configuration ProtectKeysWith* . Considérez l’exemple ci-dessous, qui stocke les clés sur un partage UNC et chiffre ces clés au repos avec un certificat X.509 spécifique :

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
        .ProtectKeysWithCertificate(Configuration["Thumbprint"]);
}

Vous pouvez fournir un X509Certificate2 à ProtectKeysWithCertificate, tel qu’un certificat chargé à partir d’un fichier :

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
        .ProtectKeysWithCertificate(
            new X509Certificate2("certificate.pfx", Configuration["Thumbprint"]));
}

Pour plus d’exemples et une discussion sur les mécanismes de chiffrement de clés intégrés, consultez Chiffrement de clé au repos.

UnprotectKeysWithAnyCertificate

Vous pouvez faire pivoter les certificats et déchiffrer les clés au repos à l’aide d’un tableau de X509Certificate2 certificats avec UnprotectKeysWithAnyCertificate :

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
        .ProtectKeysWithCertificate(
            new X509Certificate2("certificate.pfx", Configuration["MyPasswordKey"));
        .UnprotectKeysWithAnyCertificate(
            new X509Certificate2("certificate_old_1.pfx", Configuration["MyPasswordKey_1"]),
            new X509Certificate2("certificate_old_2.pfx", Configuration["MyPasswordKey_2"]));
}

SetDefaultKeyLifetime

Pour configurer le système afin qu’il utilise une durée de vie de clé de 14 jours au lieu des 90 jours par défaut, utilisez SetDefaultKeyLifetime :

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .SetDefaultKeyLifetime(TimeSpan.FromDays(14));
}

SetApplicationName

Par défaut, le système de protection des données isole les applications les unes des autres en fonction de leurs chemins racine de contenu, même si elles partagent le même référentiel de clés physiques. Cette isolation empêche les applications de comprendre les charges utiles protégées des autres.

Pour partager des charges utiles protégées entre les applications :

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .SetApplicationName("shared app name");
}

SetApplicationName définit DataProtectionOptions.ApplicationDiscriminatoren interne. Pour plus d’informations sur l’utilisation du discriminateur, consultez les sections suivantes plus loin dans cet article :

DisableAutomaticKeyGeneration

Vous pouvez avoir un scénario dans lequel vous ne souhaitez pas qu’une application restaure automatiquement les clés (créer de nouvelles clés) à mesure qu’elles approchent de l’expiration. Un exemple de ce scénario peut être les applications configurées dans une relation primaire/secondaire, où seule l’application principale est responsable des problèmes de gestion des clés et où les applications secondaires ont simplement une vue en lecture seule de l’anneau de clés. Les applications secondaires peuvent être configurées pour traiter l’anneau de clés en lecture seule en configurant le système avec DisableAutomaticKeyGeneration :

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .DisableAutomaticKeyGeneration();
}

Isolation par application

Lorsque le système de protection des données est fourni par un hôte ASP.NET Core, il isole automatiquement les applications les unes des autres, même si ces applications s’exécutent sous le même compte de processus de travail et utilisent le même master matériel de clé. Ceci est similaire au modificateur IsolateApps de l’élément <machineKey> System.Web.

Le mécanisme d’isolation fonctionne en considérant chaque application sur l’ordinateur local comme un locataire unique. Par conséquent, le IDataProtector rooté d’une application donnée inclut automatiquement l’ID d’application comme un discriminateur (ApplicationDiscriminator). L’ID unique de l’application est le chemin physique de l’application :

  • Pour les applications hébergées dans IIS, l’ID unique est le chemin physique IIS de l’application. Si une application est déployée dans un environnement de batterie de serveurs web, cette valeur est stable en supposant que les environnements IIS sont configurés de la même manière sur toutes les machines de la batterie de serveurs web.
  • Pour les applications auto-hébergées s’exécutant sur le Kestrel serveur, l’ID unique est le chemin physique de l’application sur le disque.

L’identificateur unique est conçu pour survivre aux réinitialisations, à la fois de l’application individuelle et de la machine elle-même.

Ce mécanisme d’isolation suppose que les applications ne sont pas malveillantes. Une application malveillante peut toujours avoir un impact sur toute autre application s’exécutant sous le même compte de processus de travail. Dans un environnement d’hébergement partagé où les applications ne sont pas approuvées mutuellement, le fournisseur d’hébergement doit prendre des mesures pour garantir l’isolation au niveau du système d’exploitation entre les applications, y compris la séparation des dépôts de clés sous-jacents des applications.

Si le système de protection des données n’est pas fourni par un hôte ASP.NET Core (par exemple, si vous l’instanciez via le type concret DataProtectionProvider), l’isolation de l’application est désactivée par défaut. Lorsque l’isolation d’application est désactivée, toutes les applications soutenues par le même matériel de clé peuvent partager des charges utiles tant qu’elles fournissent les objectifs appropriés. Pour fournir une isolation d’application dans cet environnement, appelez la méthode SetApplicationName sur l’objet de configuration et fournissez un nom unique pour chaque application.

Protection des données et isolation des applications

Tenez compte des points suivants pour l’isolation des applications :

  • Lorsque plusieurs applications sont pointées vers le même référentiel de clés, l’intention est que les applications partagent le même master matériel clé. La protection des données est développée en supposant que toutes les applications partageant un anneau de clés peuvent accéder à tous les éléments de cet anneau de clés. L’identificateur unique de l’application est utilisé pour isoler les clés spécifiques à l’application dérivées des clés fournies par l’anneau de clés. Il ne s’attend pas à ce que les autorisations de niveau élément, telles que celles fournies par Azure KeyVault, soient utilisées pour appliquer une isolation supplémentaire. La tentative d’autorisations au niveau de l’élément génère des erreurs d’application. Si vous ne souhaitez pas vous fier à l’isolation intégrée de l’application, des emplacements de magasin de clés distincts doivent être utilisés et non partagés entre les applications.

  • Le discriminateur d’application (ApplicationDiscriminator) permet à différentes applications de partager le même master matériel clé, mais de conserver leurs charges utiles de chiffrement distinctes les unes des autres. Pour que les applications puissent lire les charges utiles de chiffrement de l’autre, elles doivent avoir le même discriminateur d’application, qui peut être défini en appelant SetApplicationName.

  • Si une application est compromise (par exemple, par une attaque RCE), tous les master matériel clé accessibles à cette application doivent également être considérés comme compromis, quel que soit son état de protection au repos. Cela implique que si deux applications sont pointées vers le même dépôt, même si elles utilisent des discriminateurs d’application différents, une compromission de l’une d’elles équivaut fonctionnellement à une compromission des deux.

    Cette clause « fonctionnellement équivalente à un compromis des deux » est valable même si les deux applications utilisent des mécanismes différents pour la protection des clés au repos. En règle générale, il ne s’agit pas d’une configuration attendue. Le mécanisme de protection au repos est destiné à fournir une protection dans le cas où un adversaire obtient un accès en lecture au dépôt. Un adversaire qui obtient un accès en écriture au dépôt (peut-être parce qu’il a obtenu l’autorisation d’exécution de code au sein d’une application) peut insérer des clés malveillantes dans le stockage. Le système de protection des données ne fournit intentionnellement pas de protection contre un adversaire qui obtient un accès en écriture au dépôt de clés.

  • Si les applications doivent rester vraiment isolées les unes des autres, elles doivent utiliser différents référentiels de clés. Cela s’inscrit naturellement dans la définition de « isolé ». Les applications ne sont pas isolées si elles disposent toutes d’un accès en lecture et en écriture aux magasins de données des autres.

Modification d’algorithmes avec UseCryptographicAlgorithms

La pile de protection des données vous permet de modifier l’algorithme par défaut utilisé par les clés nouvellement générées. Le moyen le plus simple d’effectuer cette opération consiste à appeler UseCryptographicAlgorithms à partir du rappel de configuration :

services.AddDataProtection()
    .UseCryptographicAlgorithms(
        new AuthenticatedEncryptorConfiguration()
    {
        EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
        ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
    });

Le EncryptionAlgorithm par défaut est AES-256-CBC, et l’élément ValidationAlgorithm par défaut est HMACSHA256. La stratégie par défaut peut être définie par un administrateur système via une stratégie à l’échelle de l’ordinateur, mais un appel explicite à UseCryptographicAlgorithms remplace la stratégie par défaut.

L’appel UseCryptographicAlgorithms vous permet de spécifier l’algorithme souhaité à partir d’une liste prédéfinie intégrée. Vous n’avez pas à vous soucier de l’implémentation de l’algorithme. Dans le scénario ci-dessus, le système de protection des données tente d’utiliser l’implémentation CNG d’AES en cas d’exécution sur Windows. Sinon, elle revient à la classe managée System.Security.Cryptography.Aes.

Vous pouvez spécifier manuellement une implémentation via un appel à UseCustomCryptographicAlgorithms.

Conseil

La modification des algorithmes n’affecte pas les clés existantes dans l’anneau de clés. Elle affecte uniquement les clés nouvellement générées.

Spécification d’algorithmes managés personnalisés

Pour spécifier des algorithmes managés personnalisés, créez une instance ManagedAuthenticatedEncryptorConfiguration qui pointe vers les types d’implémentation :

serviceCollection.AddDataProtection()
    .UseCustomCryptographicAlgorithms(
        new ManagedAuthenticatedEncryptorConfiguration()
    {
        // A type that subclasses SymmetricAlgorithm
        EncryptionAlgorithmType = typeof(Aes),

        // Specified in bits
        EncryptionAlgorithmKeySize = 256,

        // A type that subclasses KeyedHashAlgorithm
        ValidationAlgorithmType = typeof(HMACSHA256)
    });

En règle générale, les propriétés *Type doivent pointer vers des implémentations concrètes et instanciables (via un ctor public sans paramètre) de SymmetricAlgorithm et KeyedHashAlgorithm, bien que les cas spéciaux du système, certaines valeurs comme typeof(Aes) pour des raisons pratiques.

Notes

SymmetricAlgorithm doit avoir une longueur de clé de ≥ 128 bits et une taille de bloc de ≥ 64 bits, et il doit prendre en charge le chiffrement en mode CBC avec remplissage PKCS #7. KeyedHashAlgorithm doit avoir une taille de synthèse de >= 128 bits, et il doit prendre en charge des clés de longueur égale à la longueur de synthèse de l’algorithme de hachage. KeyedHashAlgorithm n’est pas strictement requis pour être HMAC.

Spécification d’algorithmes CNG Windows personnalisés

Pour spécifier un algorithme CNG Windows personnalisé à l’aide du chiffrement en mode CBC avec validation HMAC, créez une instance CngCbcAuthenticatedEncryptorConfiguration qui contient les informations algorithmiques :

services.AddDataProtection()
    .UseCustomCryptographicAlgorithms(
        new CngCbcAuthenticatedEncryptorConfiguration()
    {
        // Passed to BCryptOpenAlgorithmProvider
        EncryptionAlgorithm = "AES",
        EncryptionAlgorithmProvider = null,

        // Specified in bits
        EncryptionAlgorithmKeySize = 256,

        // Passed to BCryptOpenAlgorithmProvider
        HashAlgorithm = "SHA256",
        HashAlgorithmProvider = null
    });

Notes

L’algorithme de chiffrement par blocs symétriques doit avoir une longueur de clé de >= 128 bits, une taille de bloc de >= 64 bits, et il doit prendre en charge le chiffrement en mode CBC avec remplissage PKCS #7. L’algorithme de hachage doit avoir une taille de synthèse de >= 128 bits et doit prendre en charge l’ouverture avec l’indicateur BCRYPT_ALG_HANDLE_HMAC_FLAG. Les propriétés du fournisseur peuvent être définies sur null pour utiliser le fournisseur par défaut pour l’algorithme spécifié. Pour plus d'informations, voir la documentation de BCryptOpenAlgorithmProvider.

Pour spécifier un algorithme CNG Windows personnalisé à l’aide du chiffrement Galois/Counter Mode avec validation, créez une instance CngGcmAuthenticatedEncryptorConfiguration qui contient les informations algorithmiques :

services.AddDataProtection()
    .UseCustomCryptographicAlgorithms(
        new CngGcmAuthenticatedEncryptorConfiguration()
    {
        // Passed to BCryptOpenAlgorithmProvider
        EncryptionAlgorithm = "AES",
        EncryptionAlgorithmProvider = null,

        // Specified in bits
        EncryptionAlgorithmKeySize = 256
    });

Notes

L’algorithme de chiffrement par blocs symétriques doit avoir une longueur de clé de >= 128 bits, une taille de bloc d’exactement 128 bits, et il doit prendre en charge le chiffrement GCM. Vous pouvez définir la propriété sur EncryptionAlgorithmProvider null pour utiliser le fournisseur par défaut pour l’algorithme spécifié. Pour plus d'informations, voir la documentation de BCryptOpenAlgorithmProvider.

Spécification d’autres algorithmes personnalisés

Bien qu’il ne soit pas exposé en tant qu’API de première classe, le système de protection des données est suffisamment extensible pour permettre de spécifier presque n’importe quel type d’algorithme. Par exemple, il est possible de conserver toutes les clés contenues dans un module de sécurité matérielle (HSM) et de fournir une implémentation personnalisée des principales routines de chiffrement et de déchiffrement. Pour plus d’informations, consultez IAuthenticatedEncryptordansExtensibilité du chiffrement cœur.

Conservation des clés lors de l’hébergement dans un conteneur Docker

Lors de l’hébergement dans un conteneur Docker, les clés doivent être conservées dans :

  • Un Dossier qui est un volume Docker qui persiste au-delà de la durée de vie du conteneur, tel qu’un volume partagé ou un volume monté sur un hôte.
  • Un fournisseur externe, tel que Stockage Blob Azure (illustré dans la section ProtectKeysWithAzureKeyVault) ou Redis.

Conservation des clés avec Redis

Seules les versions de Redis prenant en charge la persistance des données Redis doivent être utilisées pour stocker des clés. Le stockage Blob Azure est persistant et peut être utilisé pour stocker des clés. Pour plus d’informations, consultez ce problème GitHub.

Journalisation de DataProtection

Activez Information la journalisation de niveau de DataProtection pour faciliter le diagnostic du problème. Le fichier suivant appsettings.json active la journalisation des informations de l’API DataProtection :

{
  "Logging": {
    "LogLevel": {
      "Microsoft.AspNetCore.DataProtection": "Information"
    }
  }
}

Pour plus d’informations sur la journalisation, consultez Journalisation dans .NET Core et ASP.NET Core.

Ressources supplémentaires