Retirer la protection aux charges utiles dont les clés ont été révoquées dans ASP.NET Core
Les API de protection des données ASP.NET Core ne sont pas principalement destinées à la persistance indéfinie des charges utiles confidentielles. D’autres technologies telles que Windows CNG DPAPI et Azure Rights Management sont plus adaptées au scénario de stockage indéfini et disposent de fonctionnalités de gestion des clés fortes. Cela dit, rien n’empêche un développeur d’utiliser les API de protection des données ASP.NET Core pour la protection à long terme de données confidentielles. Les clés ne sont jamais supprimées de l’anneau de clés. IDataProtector.Unprotect
peut ainsi toujours récupérer les charges utiles existantes tant que les clés sont disponibles et valides.
Un problème survient toutefois lorsque le développeur tente de supprimer la protection des données protégées avec une clé révoquée, car IDataProtector.Unprotect
lève une exception dans ce cas. Cela peut convenir aux charges utiles temporaires ou de courte durée (comme les jetons d’authentification), car ces types de charges utiles peuvent facilement être recréés par le système et, au pire, le visiteur du site peut être amené à se reconnecter. Toutefois, pour les charges utiles persistantes, le fait que Unprotect
soit jeté peut entraîner une perte de données inacceptable.
IPersistedDataProtector
Pour prendre en charge le scénario de l’autorisation de la fin de la protection des charges utiles, même en cas de clés révoquées, le système de protection des données contient un type IPersistedDataProtector
. Pour obtenir une instance de IPersistedDataProtector
, obtenez simplement une instance de IDataProtector
de manière normale et essayez de caster le IDataProtector
sur IPersistedDataProtector
.
Notes
Toutes les instances IDataProtector
ne peuvent pas être converties en IPersistedDataProtector
. Les développeurs doivent utiliser le C# en tant qu’opérateur (ou son équivalent) pour éviter les exceptions de runtime causées par des casts non valides, et ils doivent être prêts à gérer le cas d’échec de manière appropriée.
IPersistedDataProtector
expose la surface de l’API suivante :
DangerousUnprotect(byte[] protectedData, bool ignoreRevocationErrors,
out bool requiresMigration, out bool wasRevoked) : byte[]
Cette API prend la charge utile protégée (sous forme de tableau d’octets) et retourne la charge utile non protégée. Il n’y a aucune surcharge basée sur des chaînes. Les deux paramètres sortants sont les suivants.
requiresMigration
: est défini surtrue
si la clé utilisée pour protéger cette charge utile n’est plus la clé par défaut active. Par exemple, la clé utilisée pour protéger cette charge utile est ancienne et une opération de déploiement de clés a eu lieu depuis. L’appelant peut souhaiter reprotéger la charge utile en fonction de ses besoins professionnels.wasRevoked
: est défini surtrue
si la clé utilisée pour protéger cette charge utile a été révoquée.
Avertissement
Faites preuve d’une extrême prudence lors de la transmission de ignoreRevocationErrors: true
à la méthode DangerousUnprotect
. Si, après avoir appelé cette méthode, la valeur wasRevoked
est définie sur true, la clé utilisée pour protéger cette charge utile a été révoquée et l’authenticité de la charge utile doit être traitée comme suspecte. Dans ce cas, ne continuez à utiliser la charge utile non protégée que si vous disposez d’une assurance de son authenticité, par exemple, qu’elle provient d’une base de données sécurisée au lieu d’être envoyée par un client web non approuvé.
using System;
using System.IO;
using System.Text;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.Extensions.DependencyInjection;
public class Program
{
public static void Main(string[] args)
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddDataProtection()
// point at a specific folder and use DPAPI to encrypt keys
.PersistKeysToFileSystem(new DirectoryInfo(@"c:\temp-keys"))
.ProtectKeysWithDpapi();
var services = serviceCollection.BuildServiceProvider();
// get a protector and perform a protect operation
var protector = services.GetDataProtector("Sample.DangerousUnprotect");
Console.Write("Input: ");
byte[] input = Encoding.UTF8.GetBytes(Console.ReadLine());
var protectedData = protector.Protect(input);
Console.WriteLine($"Protected payload: {Convert.ToBase64String(protectedData)}");
// demonstrate that the payload round-trips properly
var roundTripped = protector.Unprotect(protectedData);
Console.WriteLine($"Round-tripped payload: {Encoding.UTF8.GetString(roundTripped)}");
// get a reference to the key manager and revoke all keys in the key ring
var keyManager = services.GetService<IKeyManager>();
Console.WriteLine("Revoking all keys in the key ring...");
keyManager.RevokeAllKeys(DateTimeOffset.Now, "Sample revocation.");
// try calling Protect - this should throw
Console.WriteLine("Calling Unprotect...");
try
{
var unprotectedPayload = protector.Unprotect(protectedData);
Console.WriteLine($"Unprotected payload: {Encoding.UTF8.GetString(unprotectedPayload)}");
}
catch (Exception ex)
{
Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
}
// try calling DangerousUnprotect
Console.WriteLine("Calling DangerousUnprotect...");
try
{
IPersistedDataProtector persistedProtector = protector as IPersistedDataProtector;
if (persistedProtector == null)
{
throw new Exception("Can't call DangerousUnprotect.");
}
bool requiresMigration, wasRevoked;
var unprotectedPayload = persistedProtector.DangerousUnprotect(
protectedData: protectedData,
ignoreRevocationErrors: true,
requiresMigration: out requiresMigration,
wasRevoked: out wasRevoked);
Console.WriteLine($"Unprotected payload: {Encoding.UTF8.GetString(unprotectedPayload)}");
Console.WriteLine($"Requires migration = {requiresMigration}, was revoked = {wasRevoked}");
}
catch (Exception ex)
{
Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
}
}
}
/*
* SAMPLE OUTPUT
*
* Input: Hello!
* Protected payload: CfDJ8LHIzUCX1ZVBn2BZ...
* Round-tripped payload: Hello!
* Revoking all keys in the key ring...
* Calling Unprotect...
* CryptographicException: The key {...} has been revoked.
* Calling DangerousUnprotect...
* Unprotected payload: Hello!
* Requires migration = True, was revoked = True
*/