Aufheben des Schutzes von Nutzdaten, deren Schlüssel widerrufen wurden, in ASP.NET Core
Die Datenschutz-APIs von ASP.NET Core sind nicht primär für die unbegrenzte Speicherung vertraulicher Nutzdaten gedacht. Andere Technologien wie Windows CNG DPAPI und Azure Rights Management eignen sich besser für das Szenario der unbegrenzten Speicherung und verfügen über entsprechend starke Schlüsselverwaltungsfunktionen. Es spricht jedoch nichts dagegen, dass ein Entwickler die Datenschutz-APIs von ASP.NET Core für den langfristigen Schutz vertraulicher Daten nutzt. Die Schlüssel werden grundsätzlich nicht aus dem Schlüsselring entfernt, sodass IDataProtector.Unprotect
stets vorhandene Nutzdaten wiederherstellen kann, solange die Schlüssel verfügbar und gültig sind.
Ein Problem tritt jedoch auf, wenn der Entwickler versucht, den Schutz von Daten aufzuheben, die mit einem widerrufenen Schlüssel geschützt wurden, da IDataProtector.Unprotect
in diesem Fall eine Ausnahme auslöst. Dies mag für kurzlebige oder temporäre Nutzdaten (wie Authentifizierungstoken) in Ordnung sein, da diese Art von Nutzdaten leicht vom System neu erstellt werden kann und der Besucher der Website höchstens ggf. aufgefordert wird, sich erneut anzumelden. Bei gespeicherten Nutzdaten kann das Auslösen von Unprotect
jedoch zu einem unvertretbaren Datenverlust führen.
IPersistedDataProtector
Um das Szenario zu unterstützen, dass Nutzdaten auch bei widerrufenen Schlüsseln ungeschützt sein dürfen, enthält das Datensicherungssystem den Typ IPersistedDataProtector
. Um eine Instanz von IPersistedDataProtector
zu erhalten, rufen Sie einfach eine Instanz von IDataProtector
auf übliche Weise ab, und versuchen Sie, IDataProtector
in IPersistedDataProtector
umzuwandeln.
Hinweis
Nicht alle IDataProtector
-Instanzen können in IPersistedDataProtector
umgewandelt werden. Entwickler sollten C# als Operator oder ähnliches verwenden, um durch ungültige Umwandlungen verursachte Ausnahmen zu vermeiden, und sollten darauf vorbereitet sein, den Fehlerfall angemessen zu behandeln.
IPersistedDataProtector
macht die folgende API-Oberfläche verfügbar:
DangerousUnprotect(byte[] protectedData, bool ignoreRevocationErrors,
out bool requiresMigration, out bool wasRevoked) : byte[]
Diese API verwendet die geschützten Nutzdaten (als Bytearray) und gibt die ungeschützten Nutzdaten zurück. Es erfolgt keine zeichenfolgenbasierte Überladung. Die beiden Ausgabeparameter sind wie folgt.
requiresMigration
: Ist auftrue
festgelegt, wenn der zum Schutz dieser Nutzdaten verwendete Schlüssel nicht mehr der aktive Standardschlüssel ist. Zum Beispiel, wenn der Schlüssel, der zum Schutz dieser Nutzdaten verwendet wird, veraltet ist und inzwischen ein Vorgang zum Rollieren von Schlüsseln stattgefunden hat. Der Aufrufer kann je nach geschäftlichen Anforderungen einen erneuten Schutz der Nutzdaten in Betracht ziehen.wasRevoked
ist auftrue
: festgelegt, wenn der zum Schutz dieser Nutzdaten verwendete Schlüssel widerrufen wurde.
Warnung
Seien Sie äußerst vorsichtig, wenn Sie ignoreRevocationErrors: true
an die DangerousUnprotect
-Methode übergeben. Wenn nach dem Aufruf dieser Methode der Wert wasRevoked
TRUE ist, wurde der zum Schutz dieser Nutzdaten verwendete Schlüssel widerrufen, und die Authentizität der Nutzdaten ist als fragwürdig zu betrachten. In diesem Fall sollten Sie den Vorgang mit den ungeschützten Nutzdaten nur dann fortsetzen, wenn Sie eine zusätzliche Bestätigung haben, dass sie authentisch sind, z. B. dass sie aus einer sicheren Datenbank stammen und nicht von einem nicht vertrauenswürdigen Webclient gesendet wurden.
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
*/
ASP.NET Core