Configurare la protezione dati di ASP.NET Core

Quando il sistema di protezione dati viene inizializzato, applica le impostazioni predefinite in base all'ambiente operativo. Queste impostazioni sono appropriate per le app in esecuzione in un singolo computer. Tuttavia, esistono casi in cui uno sviluppatore può voler modificare le impostazioni predefinite:

  • L'app viene distribuita in più computer.
  • Per motivi di conformità.

Per questi scenari, il sistema di protezione dei dati offre un'API di configurazione avanzata.

Avviso

Analogamente ai file di configurazione, l'anello della chiave di protezione dei dati deve essere protetto usando le autorizzazioni appropriate. È possibile scegliere di crittografare le chiavi inattive, ma ciò non impedisce agli utenti malintenzionati di creare nuove chiavi. Di conseguenza, la sicurezza dell'app è interessata. Il percorso di archiviazione configurato con Protezione dati deve avere accesso limitato all'app stessa, in modo analogo al modo in cui si proteggerebbero i file di configurazione. Ad esempio, se si sceglie di archiviare l'anello di chiave su disco, usare le autorizzazioni del file system. Verificare che solo l'identità con cui viene eseguita l'app Web abbia accesso in lettura, scrittura e creazione dell'accesso a tale directory. Se si usa Archiviazione BLOB di Azure, solo l'app Web deve avere la possibilità di leggere, scrivere o creare nuove voci nell'archivio BLOB e così via.

Il metodo AddDataProtection di estensione restituisce un oggetto IDataProtectionBuilder. IDataProtectionBuilder espone i metodi di estensione che è possibile concatenare per configurare le opzioni di protezione dei dati.

I pacchetti NuGet seguenti sono necessari per le estensioni di protezione dei dati usate in questo articolo:

ProtectKeysWithAzureKeyVault

Accedere ad Azure usando l'interfaccia della riga di comando, ad esempio:

az login

Per gestire le chiavi con Azure Key Vault, configurare il sistema con ProtectKeysWithAzureKeyVault in Program.cs. blobUriWithSasToken è l'URI completo in cui deve essere archiviato il file di chiave. L'URI deve contenere il token di firma di accesso condiviso come parametro della stringa di query:

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

Per consentire a un'app di comunicare e autorizzarsi con KeyVault, è necessario aggiungere il pacchetto Azure.Identity

Impostare la posizione di archiviazione dell'anello di chiave , ad esempio PersistKeysToAzureBlobStorage. Il percorso deve essere impostato perché la chiamata ProtectKeysWithAzureKeyVault implementa un oggetto IXmlEncryptor che disabilita le impostazioni di protezione automatica dei dati, inclusa la posizione di archiviazione dell'anello di chiave. L'esempio precedente usa Archiviazione BLOB di Azure per rendere persistente l'anello di tasti. Per altre informazioni, vedere Provider di archiviazione chiavi: Archiviazione di Azure. È anche possibile rendere persistente l'anello di tasti in locale con PersistKeysToFileSystem.

keyIdentifier è l'identificatore di chiave dell'insieme di credenziali delle chiavi usato per la crittografia della chiave. Ad esempio, una chiave creata nell'insieme contosokeyvault di credenziali delle chiavi denominato dataprotection in ha l'identificatore https://contosokeyvault.vault.azure.net/keys/dataprotection/di chiave . Fornire all'app le autorizzazioni Get, Unwrap Key e Wrap Key per l'insieme di credenziali delle chiavi.

ProtectKeysWithAzureKeyVault Overload:

Se l'app usa i pacchetti di Azure precedenti (Microsoft.AspNetCore.DataProtection.Azure Archiviazione e Microsoft.AspNetCore.DataProtection.AzureKeyVault), è consigliabile rimuovere questi riferimenti e eseguire l'aggiornamento ad Azure.Extensions.AspNetCore.DataProtection.Blobs e Azure.Extensions.AspNetCore.DataProtection.Keys. Questi pacchetti sono la posizione in cui vengono forniti nuovi aggiornamenti e risolvere alcuni problemi principali di sicurezza e stabilità con i pacchetti meno recenti.

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

Per archiviare le chiavi in una condivisione UNC anziché nel percorso predefinito %LOCALAPPDATA% , configurare il sistema con PersistKeysToFileSystem:

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

Avviso

Se si modifica il percorso di persistenza della chiave, il sistema non crittografa più automaticamente le chiavi inattive, perché non sa se DPAPI è un meccanismo di crittografia appropriato.

PersistKeysToDbContext

Per archiviare le chiavi in un database usando EntityFramework, configurare il sistema con il pacchetto Microsoft.AspNetCore.DataProtection.EntityFrameworkCore :

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

Il codice precedente archivia le chiavi nel database configurato. Il contesto del database utilizzato deve implementare IDataProtectionKeyContext. IDataProtectionKeyContext espone la proprietà DataProtectionKeys

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

Questa proprietà rappresenta la tabella in cui vengono archiviate le chiavi. Creare la tabella manualmente o con DbContext Migrazioni. Per ulteriori informazioni, vedere DataProtectionKey.

ProtectKeysWith*

È possibile configurare il sistema per proteggere le chiavi inattive chiamando una delle API di configurazione ProtectKeysWith* . Si consideri l'esempio seguente, che archivia le chiavi in una condivisione UNC e crittografa tali chiavi inattive con un certificato X.509 specifico:

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

È possibile fornire un oggetto X509Certificate2 a ProtectKeysWithCertificate, ad esempio un certificato caricato da un file:

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

Per altri esempi e informazioni sui meccanismi di crittografia delle chiavi predefiniti, vedere Crittografia delle chiavi inattivi .

UnprotectKeysWithAnyCertificate

È possibile ruotare i certificati e decrittografare le chiavi inattive usando una matrice di X509Certificate2 certificati con 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

Per configurare il sistema per l'uso di una durata chiave di 14 giorni anziché di 90 giorni predefiniti, usare SetDefaultKeyLifetime:

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

SetApplicationName

Per impostazione predefinita, il sistema di protezione dei dati isola le app l'una dall'altra in base ai percorsi radice del contenuto, anche se condividono lo stesso repository di chiavi fisiche. Questo isolamento impedisce alle app di comprendere i payload protetti tra loro.

Per condividere payload protetti tra le app:

  • Configurare SetApplicationName in ogni app con lo stesso valore.
  • Usare la stessa versione dello stack di API Protezione dati tra le app. Eseguire una delle operazioni seguenti nei file di progetto delle app:
    • Fare riferimento alla stessa versione del framework condiviso tramite il metapacchetto Microsoft.AspNetCore.App.
    • Fare riferimento alla stessa versione del pacchetto di protezione dei dati.
builder.Services.AddDataProtection()
    .SetApplicationName("<sharedApplicationName>");

SetApplicationName internamente imposta DataProtectionOptions.ApplicationDiscriminator. Ai fini della risoluzione dei problemi, il valore assegnato al discriminatorio dal framework può essere registrato con il codice seguente inserito dopo la WebApplication compilazione di Program.cs:

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

Per altre informazioni sull'uso del discriminatorio, vedere le sezioni seguenti più avanti in questo articolo:

Avviso

In .NET 6 WebApplicationBuilder normalizza il percorso radice del contenuto per terminare con un oggetto DirectorySeparatorChar. Ad esempio, in Windows il percorso radice del contenuto termina in e in \ Linux /. Gli altri host non normalizzano il percorso. La maggior parte delle app che eseguono la migrazione da HostBuilder o WebHostBuilder non condividerà lo stesso nome dell'app perché non avrà la terminazione DirectorySeparatorChar. Per risolvere questo problema, rimuovere il carattere separatore di directory e impostare manualmente il nome dell'app, come illustrato nel codice seguente:

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

Si potrebbe avere uno scenario in cui non si vuole che un'app rollback automatico delle chiavi (creare nuove chiavi) man mano che si avvicinano alla scadenza. Un esempio di questo scenario potrebbe essere costituito dalle app configurate in una relazione primaria/secondaria, in cui solo l'app primaria è responsabile dei problemi di gestione delle chiavi e le app secondarie hanno semplicemente una visualizzazione di sola lettura dell'anello chiave. Le app secondarie possono essere configurate per considerare l'anello di chiave come di sola lettura configurando il sistema con DisableAutomaticKeyGeneration:

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

Isolamento per applicazione

Quando il sistema di protezione dei dati viene fornito da un host ASP.NET Core, isola automaticamente le app l'una dall'altra, anche se tali app vengono eseguite con lo stesso account del processo di lavoro e usano lo stesso materiale di master keying. È simile al modificatore IsolateApps dall'elemento system.Web <machineKey> .

Il meccanismo di isolamento funziona considerando ogni app nel computer locale come tenant univoco, pertanto la IDataProtector radice per qualsiasi app specifica include automaticamente l'ID app come discriminante (ApplicationDiscriminator). L'ID univoco dell'app è il percorso fisico dell'app:

  • Per le app ospitate in IIS, l'ID univoco è il percorso fisico IIS dell'app. Se un'app viene distribuita in un ambiente web farm, questo valore è stabile presupponendo che gli ambienti IIS siano configurati in modo analogo in tutti i computer della Web farm.
  • Per le app self-hosted in esecuzione nel Kestrel server, l'ID univoco è il percorso fisico dell'app su disco.

L'identificatore univoco è progettato per sopravvivere alle reimpostazioni, sia della singola app che del computer stesso.

Questo meccanismo di isolamento presuppone che le app non siano dannose. Un'app dannosa può sempre influire su qualsiasi altra app in esecuzione con lo stesso account del processo di lavoro. In un ambiente di hosting condiviso in cui le app non sono attendibili a vicenda, il provider di hosting deve adottare misure per garantire l'isolamento a livello di sistema operativo tra le app, inclusa la separazione dei repository delle chiavi sottostanti delle app.

Se il sistema di protezione dati non viene fornito da un host core ASP.NET (ad esempio, se si crea un'istanza tramite il tipo concreto) l'isolamento DataProtectionProvider dell'app è disabilitato per impostazione predefinita. Quando l'isolamento dell'app è disabilitato, tutte le app supportate dallo stesso materiale di keying possono condividere i payload purché forniscano gli scopi appropriati. Per fornire l'isolamento dell'app in questo ambiente, chiamare il SetApplicationName metodo sull'oggetto di configurazione e specificare un nome univoco per ogni app.

Protezione dei dati e isolamento delle app

Considerare i punti seguenti per l'isolamento delle app:

  • Quando più app puntano allo stesso repository di chiavi, l'intenzione è che le app condividono lo stesso materiale della chiave master. La protezione dei dati viene sviluppata presupponendo che tutte le app che condividono un anello di chiave possano accedere a tutti gli elementi dell'anello di chiave. L'identificatore univoco dell'applicazione viene usato per isolare le chiavi specifiche dell'applicazione derivate dalle chiavi fornite dall'anello di chiave. Non prevede autorizzazioni a livello di elemento, ad esempio quelle fornite da Azure KeyVault per applicare l'isolamento aggiuntivo. Il tentativo di autorizzazioni a livello di elemento genera errori dell'applicazione. Se non si vuole basarsi sull'isolamento predefinito dell'applicazione, è consigliabile usare posizioni separate dell'archivio chiavi e non condividere tra le applicazioni.

  • Il discriminante dell'applicazione (ApplicationDiscriminator) viene usato per consentire alle app diverse di condividere lo stesso materiale della chiave master, ma di mantenere i payload crittografici distinti l'uno dall'altro. Affinché le app possano leggere i payload crittografici degli altri, devono avere lo stesso discriminante dell'applicazione, che può essere impostato chiamando SetApplicationName.

  • Se un'app viene compromessa (ad esempio, da un attacco RCE), tutto il materiale della chiave master accessibile a tale app deve essere considerato compromesso, indipendentemente dal relativo stato di protezione inattivo. Ciò implica che se due app puntano allo stesso repository, anche se usano diversi discriminatori di app, un compromesso di uno è funzionalmente equivalente a un compromesso di entrambi.

    Questa clausola "equivalente funzionalmente a una compromissione di entrambe" contiene anche se le due app usano meccanismi diversi per la protezione delle chiavi inattivi. In genere, questa non è una configurazione prevista. Il meccanismo di protezione dei dati inattivi è progettato per fornire protezione nel caso in cui un avversario ottiene l'accesso in lettura al repository. Un antagonista che ottiene l'accesso in scrittura al repository (forse perché ha ottenuto l'autorizzazione di esecuzione del codice all'interno di un'app) può inserire chiavi dannose nell'archiviazione. Il sistema di protezione dei dati non fornisce intenzionalmente protezione da un antagonista che ottiene l'accesso in scrittura al repository delle chiavi.

  • Se le app devono rimanere veramente isolate l'una dall'altra, devono usare repository di chiavi diversi. Ciò non rientra naturalmente nella definizione di "isolato". Le app non sono isolate se hanno tutti accesso in lettura e scrittura agli archivi dati dell'altro.

Modifica degli algoritmi con UseCryptographicAlgorithms

Lo stack di protezione dati consente di modificare l'algoritmo predefinito usato dalle chiavi appena generate. Il modo più semplice per eseguire questa operazione consiste nel chiamare UseCryptographicAlgorithms dal callback di configurazione:

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

Il valore predefinito di EncryptionAlgorithm è AES-256-CBC e il valore predefinito ValidationAlgorithm è HMACSHA256. I criteri predefiniti possono essere impostati da un amministratore di sistema tramite criteri a livello di computer, ma una chiamata esplicita per UseCryptographicAlgorithms eseguire l'override dei criteri predefiniti.

La chiamata UseCryptographicAlgorithms consente di specificare l'algoritmo desiderato da un elenco predefinito. Non è necessario preoccuparsi dell'implementazione dell'algoritmo. Nello scenario precedente, il sistema di protezione dei dati tenta di usare l'implementazione CNG di AES se in esecuzione in Windows. In caso contrario, esegue il fallback alla classe gestita System.Security.Cryptography.Aes .

È possibile specificare manualmente un'implementazione tramite una chiamata a UseCustomCryptographicAlgorithms.

Suggerimento

La modifica degli algoritmi non influisce sulle chiavi esistenti nell'anello di tasti. Influisce solo sulle chiavi appena generate.

Specifica di algoritmi gestiti personalizzati

Per specificare algoritmi gestiti personalizzati, creare un'istanza ManagedAuthenticatedEncryptorConfiguration che punti ai tipi di implementazione:

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)
    });

In genere, le proprietà *Type devono puntare a implementazioni concrete, creabili (tramite un ctor senza parametri pubblico) di SymmetricAlgorithm e KeyedHashAlgorithm, anche se i casi speciali del sistema alcuni valori come typeof(Aes) per praticità.

Nota

SymmetricAlgorithm deve avere una lunghezza della chiave di ≥ 128 bit e una dimensione del blocco di ≥ 64 bit e deve supportare la crittografia in modalità CBC con spaziatura interna PKCS #7. KeyedHashAlgorithm deve avere una dimensione digest pari >a = 128 bit e deve supportare chiavi di lunghezza uguale alla lunghezza del digest dell'algoritmo hash. KeyedHashAlgorithm non è strettamente necessario essere HMAC.

Specifica di algoritmi CNG windows personalizzati

Per specificare un algoritmo CNG di Windows personalizzato usando la crittografia in modalità CBC con convalida HMAC, creare un'istanza CngCbcAuthenticatedEncryptorConfiguration contenente le informazioni algoritmiche:

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
    });

Nota

L'algoritmo di crittografia a blocchi simmetrici deve avere una lunghezza della >chiave = 128 bit, una dimensione del blocco = >64 bit e deve supportare la crittografia in modalità CBC con spaziatura interna PKCS #7. L'algoritmo hash deve avere una dimensione del digest = >128 bit e deve supportare l'apertura con il flag BCRYPT_ALG_HANDLE_HMAC_FLAG. Le proprietà *Provider possono essere impostate su null per usare il provider predefinito per l'algoritmo specificato. Per altre informazioni, vedere la documentazione di BCryptOpenAlgorithmProvider .

Per specificare un algoritmo CNG di Windows personalizzato usando la crittografia galois/modalità contatore con la convalida, creare un'istanza CngGcmAuthenticatedEncryptorConfiguration contenente le informazioni algoritmiche:

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

        // Specified in bits
        EncryptionAlgorithmKeySize = 256
    });

Nota

L'algoritmo di crittografia a blocchi simmetrici deve avere una lunghezza della >chiave = 128 bit, una dimensione del blocco di esattamente 128 bit e deve supportare la crittografia GCM. È possibile impostare la EncryptionAlgorithmProvider proprietà su Null per utilizzare il provider predefinito per l'algoritmo specificato. Per altre informazioni, vedere la documentazione di BCryptOpenAlgorithmProvider .

Specifica di altri algoritmi personalizzati

Anche se non esposto come API di prima classe, il sistema di protezione dei dati è sufficientemente estensibile per consentire di specificare quasi qualsiasi tipo di algoritmo. Ad esempio, è possibile mantenere tutte le chiavi contenute in un modulo HSM (Hardware Security Module) e fornire un'implementazione personalizzata delle routine di crittografia e decrittografia principali. Per altre informazioni, vedere l'articolo IAuthenticatedEncryptor relativo all'estendibilità della crittografia core.

Salvataggio permanente delle chiavi durante l'hosting in un contenitore Docker

Quando si ospita in un contenitore Docker , le chiavi devono essere mantenute in uno dei due casi seguenti:

Salvataggio permanente delle chiavi con Redis

Per archiviare le chiavi è necessario usare solo le versioni redis che supportano la persistenza dei dati Redis. L'archiviazione BLOB di Azure è persistente e può essere usata per archiviare le chiavi. Per altre informazioni, vedere questo problema in GitHub.

Registrazione della protezione dei dati

Abilitare la Information registrazione a livello di DataProtection per facilitare la diagnosi del problema. Il file seguente appsettings.json abilita la registrazione delle informazioni dell'API DataProtection:

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

Per altre informazioni sulla registrazione, vedere Registrazione in .NET Core e ASP.NET Core.

Risorse aggiuntive

Quando il sistema di protezione dati viene inizializzato, applica le impostazioni predefinite in base all'ambiente operativo. Queste impostazioni sono appropriate per le app in esecuzione in un singolo computer. Tuttavia, esistono casi in cui uno sviluppatore può voler modificare le impostazioni predefinite:

  • L'app viene distribuita in più computer.
  • Per motivi di conformità.

Per questi scenari, il sistema di protezione dei dati offre un'API di configurazione avanzata.

Avviso

Analogamente ai file di configurazione, l'anello della chiave di protezione dei dati deve essere protetto usando le autorizzazioni appropriate. È possibile scegliere di crittografare le chiavi inattive, ma ciò non impedisce agli utenti malintenzionati di creare nuove chiavi. Di conseguenza, la sicurezza dell'app è interessata. Il percorso di archiviazione configurato con Protezione dati deve avere accesso limitato all'app stessa, in modo analogo al modo in cui si proteggerebbero i file di configurazione. Ad esempio, se si sceglie di archiviare l'anello di chiave su disco, usare le autorizzazioni del file system. Verificare che solo l'identità con cui viene eseguita l'app Web abbia accesso in lettura, scrittura e creazione dell'accesso a tale directory. Se si usa Archiviazione BLOB di Azure, solo l'app Web deve avere la possibilità di leggere, scrivere o creare nuove voci nell'archivio BLOB e così via.

Il metodo AddDataProtection di estensione restituisce un oggetto IDataProtectionBuilder. IDataProtectionBuilder espone i metodi di estensione che è possibile concatenare per configurare le opzioni di protezione dei dati.

I pacchetti NuGet seguenti sono necessari per le estensioni di protezione dei dati usate in questo articolo:

ProtectKeysWithAzureKeyVault

Accedere ad Azure usando l'interfaccia della riga di comando, ad esempio:

az login

Per archiviare le chiavi in Azure Key Vault, configurare il sistema con ProtectKeysWithAzureKeyVault nella Startup classe . blobUriWithSasToken è l'URI completo in cui deve essere archiviato il file di chiave. L'URI deve contenere il token di firma di accesso condiviso come parametro della stringa di query:

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

Per consentire a un'app di comunicare e autorizzarsi con KeyVault, è necessario aggiungere il pacchetto Azure.Identity

Impostare la posizione di archiviazione dell'anello di chiave , ad esempio PersistKeysToAzureBlobStorage. Il percorso deve essere impostato perché la chiamata ProtectKeysWithAzureKeyVault implementa un oggetto IXmlEncryptor che disabilita le impostazioni di protezione automatica dei dati, inclusa la posizione di archiviazione dell'anello di chiave. L'esempio precedente usa Archiviazione BLOB di Azure per rendere persistente l'anello di tasti. Per altre informazioni, vedere Provider di archiviazione chiavi: Archiviazione di Azure. È anche possibile rendere persistente l'anello di tasti in locale con PersistKeysToFileSystem.

keyIdentifier è l'identificatore di chiave dell'insieme di credenziali delle chiavi usato per la crittografia della chiave. Ad esempio, una chiave creata nell'insieme contosokeyvault di credenziali delle chiavi denominato dataprotection in ha l'identificatore https://contosokeyvault.vault.azure.net/keys/dataprotection/di chiave . Fornire all'app le autorizzazioni Get, Unwrap Key e Wrap Key per l'insieme di credenziali delle chiavi.

ProtectKeysWithAzureKeyVault Overload:

Se l'app usa i pacchetti di Azure precedenti (Microsoft.AspNetCore.DataProtection.Azure Archiviazione e Microsoft.AspNetCore.DataProtection.AzureKeyVault), è consigliabile rimuovere questi riferimenti e eseguire l'aggiornamento ad Azure.Extensions.AspNetCore.DataProtection.Blobs e Azure.Extensions.AspNetCore.DataProtection.Keys. Questi pacchetti sono la posizione in cui vengono forniti nuovi aggiornamenti e risolvere alcuni problemi principali di sicurezza e stabilità con i pacchetti meno recenti.

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

Per archiviare le chiavi in una condivisione UNC anziché nel percorso predefinito %LOCALAPPDATA% , configurare il sistema con PersistKeysToFileSystem:

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

Avviso

Se si modifica il percorso di persistenza della chiave, il sistema non crittografa più automaticamente le chiavi inattive, perché non sa se DPAPI è un meccanismo di crittografia appropriato.

PersistKeysToDbContext

Per archiviare le chiavi in un database usando EntityFramework, configurare il sistema con il pacchetto Microsoft.AspNetCore.DataProtection.EntityFrameworkCore :

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

Il codice precedente archivia le chiavi nel database configurato. Il contesto del database utilizzato deve implementare IDataProtectionKeyContext. IDataProtectionKeyContext espone la proprietà DataProtectionKeys

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

Questa proprietà rappresenta la tabella in cui vengono archiviate le chiavi. Creare la tabella manualmente o con DbContext Migrazioni. Per ulteriori informazioni, vedere DataProtectionKey.

ProtectKeysWith*

È possibile configurare il sistema per proteggere le chiavi inattive chiamando una delle API di configurazione ProtectKeysWith* . Si consideri l'esempio seguente, che archivia le chiavi in una condivisione UNC e crittografa tali chiavi inattive con un certificato X.509 specifico:

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

È possibile fornire un oggetto X509Certificate2 a ProtectKeysWithCertificate, ad esempio un certificato caricato da un file:

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

Per altri esempi e informazioni sui meccanismi di crittografia delle chiavi predefiniti, vedere Crittografia delle chiavi inattivi .

UnprotectKeysWithAnyCertificate

È possibile ruotare i certificati e decrittografare le chiavi inattive usando una matrice di X509Certificate2 certificati con 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

Per configurare il sistema per l'uso di una durata chiave di 14 giorni anziché di 90 giorni predefiniti, usare SetDefaultKeyLifetime:

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

SetApplicationName

Per impostazione predefinita, il sistema di protezione dei dati isola le app l'una dall'altra in base ai percorsi radice del contenuto, anche se condividono lo stesso repository di chiavi fisiche. Questo isolamento impedisce alle app di comprendere i payload protetti tra loro.

Per condividere payload protetti tra le app:

  • Configurare SetApplicationName in ogni app con lo stesso valore.
  • Usare la stessa versione dello stack di API Protezione dati tra le app. Eseguire una delle operazioni seguenti nei file di progetto delle app:
    • Fare riferimento alla stessa versione del framework condiviso tramite il metapacchetto Microsoft.AspNetCore.App.
    • Fare riferimento alla stessa versione del pacchetto di protezione dei dati.
public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .SetApplicationName("shared app name");
}

SetApplicationName internamente imposta DataProtectionOptions.ApplicationDiscriminator. Per altre informazioni sull'uso del discriminatorio, vedere le sezioni seguenti più avanti in questo articolo:

DisableAutomaticKeyGeneration

Si potrebbe avere uno scenario in cui non si vuole che un'app rollback automatico delle chiavi (creare nuove chiavi) man mano che si avvicinano alla scadenza. Un esempio di questo scenario potrebbe essere costituito dalle app configurate in una relazione primaria/secondaria, in cui solo l'app primaria è responsabile dei problemi di gestione delle chiavi e le app secondarie hanno semplicemente una visualizzazione di sola lettura dell'anello chiave. Le app secondarie possono essere configurate per considerare l'anello di chiave come di sola lettura configurando il sistema con DisableAutomaticKeyGeneration:

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

Isolamento per applicazione

Quando il sistema di protezione dei dati viene fornito da un host ASP.NET Core, isola automaticamente le app l'una dall'altra, anche se tali app vengono eseguite con lo stesso account del processo di lavoro e usano lo stesso materiale di master keying. È simile al modificatore IsolateApps dall'elemento system.Web <machineKey> .

Il meccanismo di isolamento funziona considerando ogni app nel computer locale come tenant univoco, pertanto la IDataProtector radice per qualsiasi app specifica include automaticamente l'ID app come discriminante (ApplicationDiscriminator). L'ID univoco dell'app è il percorso fisico dell'app:

  • Per le app ospitate in IIS, l'ID univoco è il percorso fisico IIS dell'app. Se un'app viene distribuita in un ambiente web farm, questo valore è stabile presupponendo che gli ambienti IIS siano configurati in modo analogo in tutti i computer della Web farm.
  • Per le app self-hosted in esecuzione nel Kestrel server, l'ID univoco è il percorso fisico dell'app su disco.

L'identificatore univoco è progettato per sopravvivere alle reimpostazioni, sia della singola app che del computer stesso.

Questo meccanismo di isolamento presuppone che le app non siano dannose. Un'app dannosa può sempre influire su qualsiasi altra app in esecuzione con lo stesso account del processo di lavoro. In un ambiente di hosting condiviso in cui le app non sono attendibili a vicenda, il provider di hosting deve adottare misure per garantire l'isolamento a livello di sistema operativo tra le app, inclusa la separazione dei repository delle chiavi sottostanti delle app.

Se il sistema di protezione dati non viene fornito da un host core ASP.NET (ad esempio, se si crea un'istanza tramite il tipo concreto) l'isolamento DataProtectionProvider dell'app è disabilitato per impostazione predefinita. Quando l'isolamento dell'app è disabilitato, tutte le app supportate dallo stesso materiale di keying possono condividere i payload purché forniscano gli scopi appropriati. Per fornire l'isolamento dell'app in questo ambiente, chiamare il metodo SetApplicationName nell'oggetto di configurazione e specificare un nome univoco per ogni app.

Protezione dei dati e isolamento delle app

Considerare i punti seguenti per l'isolamento delle app:

  • Quando più app puntano allo stesso repository di chiavi, l'intenzione è che le app condividono lo stesso materiale della chiave master. La protezione dei dati viene sviluppata presupponendo che tutte le app che condividono un anello di chiave possano accedere a tutti gli elementi dell'anello di chiave. L'identificatore univoco dell'applicazione viene usato per isolare le chiavi specifiche dell'applicazione derivate dalle chiavi fornite dall'anello di chiave. Non prevede autorizzazioni a livello di elemento, ad esempio quelle fornite da Azure KeyVault per applicare l'isolamento aggiuntivo. Il tentativo di autorizzazioni a livello di elemento genera errori dell'applicazione. Se non si vuole basarsi sull'isolamento predefinito dell'applicazione, è consigliabile usare posizioni separate dell'archivio chiavi e non condividere tra le applicazioni.

  • Il discriminante dell'applicazione (ApplicationDiscriminator) viene usato per consentire alle app diverse di condividere lo stesso materiale della chiave master, ma di mantenere i payload crittografici distinti l'uno dall'altro. Affinché le app possano leggere i payload crittografici degli altri, devono avere lo stesso discriminante dell'applicazione, che può essere impostato chiamando SetApplicationName.

  • Se un'app viene compromessa (ad esempio, da un attacco RCE), tutto il materiale della chiave master accessibile a tale app deve essere considerato compromesso, indipendentemente dal relativo stato di protezione inattivo. Ciò implica che se due app puntano allo stesso repository, anche se usano diversi discriminatori di app, un compromesso di uno è funzionalmente equivalente a un compromesso di entrambi.

    Questa clausola "equivalente funzionalmente a una compromissione di entrambe" contiene anche se le due app usano meccanismi diversi per la protezione delle chiavi inattivi. In genere, questa non è una configurazione prevista. Il meccanismo di protezione dei dati inattivi è progettato per fornire protezione nel caso in cui un avversario ottiene l'accesso in lettura al repository. Un antagonista che ottiene l'accesso in scrittura al repository (forse perché ha ottenuto l'autorizzazione di esecuzione del codice all'interno di un'app) può inserire chiavi dannose nell'archiviazione. Il sistema di protezione dei dati non fornisce intenzionalmente protezione da un antagonista che ottiene l'accesso in scrittura al repository delle chiavi.

  • Se le app devono rimanere veramente isolate l'una dall'altra, devono usare repository di chiavi diversi. Ciò non rientra naturalmente nella definizione di "isolato". Le app non sono isolate se hanno tutti accesso in lettura e scrittura agli archivi dati dell'altro.

Modifica degli algoritmi con UseCryptographicAlgorithms

Lo stack di protezione dati consente di modificare l'algoritmo predefinito usato dalle chiavi appena generate. Il modo più semplice per eseguire questa operazione consiste nel chiamare UseCryptographicAlgorithms dal callback di configurazione:

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

Il valore predefinito di EncryptionAlgorithm è AES-256-CBC e il valore predefinito ValidationAlgorithm è HMACSHA256. I criteri predefiniti possono essere impostati da un amministratore di sistema tramite criteri a livello di computer, ma una chiamata esplicita per UseCryptographicAlgorithms eseguire l'override dei criteri predefiniti.

La chiamata UseCryptographicAlgorithms consente di specificare l'algoritmo desiderato da un elenco predefinito. Non è necessario preoccuparsi dell'implementazione dell'algoritmo. Nello scenario precedente, il sistema di protezione dei dati tenta di usare l'implementazione CNG di AES se in esecuzione in Windows. In caso contrario, esegue il fallback alla classe gestita System.Security.Cryptography.Aes .

È possibile specificare manualmente un'implementazione tramite una chiamata a UseCustomCryptographicAlgorithms.

Suggerimento

La modifica degli algoritmi non influisce sulle chiavi esistenti nell'anello di tasti. Influisce solo sulle chiavi appena generate.

Specifica di algoritmi gestiti personalizzati

Per specificare algoritmi gestiti personalizzati, creare un'istanza ManagedAuthenticatedEncryptorConfiguration che punti ai tipi di implementazione:

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)
    });

In genere, le proprietà *Type devono puntare a implementazioni concrete, creabili (tramite un ctor senza parametri pubblico) di SymmetricAlgorithm e KeyedHashAlgorithm, anche se i casi speciali del sistema alcuni valori come typeof(Aes) per praticità.

Nota

SymmetricAlgorithm deve avere una lunghezza della chiave di ≥ 128 bit e una dimensione del blocco di ≥ 64 bit e deve supportare la crittografia in modalità CBC con spaziatura interna PKCS #7. KeyedHashAlgorithm deve avere una dimensione digest pari >a = 128 bit e deve supportare chiavi di lunghezza uguale alla lunghezza del digest dell'algoritmo hash. KeyedHashAlgorithm non è strettamente necessario essere HMAC.

Specifica di algoritmi CNG windows personalizzati

Per specificare un algoritmo CNG di Windows personalizzato usando la crittografia in modalità CBC con convalida HMAC, creare un'istanza CngCbcAuthenticatedEncryptorConfiguration contenente le informazioni algoritmiche:

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

        // Specified in bits
        EncryptionAlgorithmKeySize = 256,

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

Nota

L'algoritmo di crittografia a blocchi simmetrici deve avere una lunghezza della >chiave = 128 bit, una dimensione del blocco = >64 bit e deve supportare la crittografia in modalità CBC con spaziatura interna PKCS #7. L'algoritmo hash deve avere una dimensione del digest = >128 bit e deve supportare l'apertura con il flag BCRYPT_ALG_HANDLE_HMAC_FLAG. Le proprietà *Provider possono essere impostate su null per usare il provider predefinito per l'algoritmo specificato. Per altre informazioni, vedere la documentazione di BCryptOpenAlgorithmProvider .

Per specificare un algoritmo CNG di Windows personalizzato usando la crittografia galois/modalità contatore con la convalida, creare un'istanza CngGcmAuthenticatedEncryptorConfiguration contenente le informazioni algoritmiche:

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

        // Specified in bits
        EncryptionAlgorithmKeySize = 256
    });

Nota

L'algoritmo di crittografia a blocchi simmetrici deve avere una lunghezza della >chiave = 128 bit, una dimensione del blocco di esattamente 128 bit e deve supportare la crittografia GCM. È possibile impostare la EncryptionAlgorithmProvider proprietà su Null per utilizzare il provider predefinito per l'algoritmo specificato. Per altre informazioni, vedere la documentazione di BCryptOpenAlgorithmProvider .

Specifica di altri algoritmi personalizzati

Anche se non esposto come API di prima classe, il sistema di protezione dei dati è sufficientemente estensibile per consentire di specificare quasi qualsiasi tipo di algoritmo. Ad esempio, è possibile mantenere tutte le chiavi contenute in un modulo HSM (Hardware Security Module) e fornire un'implementazione personalizzata delle routine di crittografia e decrittografia principali. Per altre informazioni, vedere l'articolo IAuthenticatedEncryptor relativo all'estendibilità della crittografia core.

Salvataggio permanente delle chiavi durante l'hosting in un contenitore Docker

Quando si ospita in un contenitore Docker , le chiavi devono essere mantenute in uno dei due casi seguenti:

Salvataggio permanente delle chiavi con Redis

Per archiviare le chiavi è necessario usare solo le versioni redis che supportano la persistenza dei dati Redis. L'archiviazione BLOB di Azure è persistente e può essere usata per archiviare le chiavi. Per altre informazioni, vedere questo problema in GitHub.

Registrazione della protezione dei dati

Abilitare la Information registrazione a livello di DataProtection per facilitare la diagnosi del problema. Il file seguente appsettings.json abilita la registrazione delle informazioni dell'API DataProtection:

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

Per altre informazioni sulla registrazione, vedere Registrazione in .NET Core e ASP.NET Core.

Risorse aggiuntive