Uso di Always Encrypted con il provider di dati Microsoft .NET per SQL Server

Si applica a: .NET Framework .NET Standard

Questo articolo include informazioni su come sviluppare applicazioni .NET usando Always Encrypted o Always Encrypted con enclave sicuri e il provider di dati Microsoft .NET per SQL Server.

Always Encrypted consente alle applicazioni client di eseguire la crittografia dei dati sensibili senza mai rivelare i dati o le chiavi di crittografia a SQL Server o al database SQL di Azure. Un driver abilitato per Always Encrypted, come il provider di dati Microsoft .NET per SQL Server, fa tutto questo crittografando e decrittografando in modo trasparente i dati sensibili nell'applicazione client. Il driver determina automaticamente i parametri di query che corrispondono a colonne di database con dati sensibili (protette con Always Encrypted) ed esegue la crittografia dei valori di questi parametri prima di passare i dati al server. Analogamente, il driver esegue in modo trasparente la decrittografia dei dati, recuperati dalle colonne di database crittografate nei risultati delle query. Per altre informazioni, vedere Sviluppare applicazioni usando Always Encrypted e Sviluppare applicazioni usando Always Encrypted con enclave sicuri.

Prerequisiti

  • Configurare Always Encrypted nel database. Questo processo implica il provisioning di chiavi Always Encrypted e la configurazione della crittografia per le colonne di database selezionate. Se non è presente un database in cui Always Encrypted è configurato, seguire le istruzioni fornite nel Tutorial: Attività iniziali con Always Encrypted.
  • Se si usa Always Encrypted con enclave sicure, vedere Sviluppare applicazioni usando Always Encrypted con enclave sicure per altri prerequisiti.
  • Verificare che nel computer di sviluppo sia installata la piattaforma .NET richiesta. Con Microsoft.Data.SqlClient la funzionalità Always Encrypted è supportata sia per .NET Framework che per .NET Core. Assicurarsi che .NET Framework 4.6 o versione successiva o .NET Core 2.1 o versione successiva sia configurato come versione della piattaforma .NET di destinazione nell'ambiente di sviluppo. A partire da Microsoft.Data.SqlClient 2.1.0 e versioni successive, la funzionalità Always Encrypted è supportata anche per .NET Standard 2.0. Per usare Always Encrypted con enclave sicure è necessario .NET Standard 2.1. Se si vogliono usare enclave VBS senza attestazione, è necessario Microsoft.Data.SqlClient versione 4.1 o successiva. Se si usa Visual Studio, vedere Panoramica sull'impostazione dei framework di destinazione.

La tabella seguente riepiloga le piattaforme .NET necessarie per usare Always Encrypted con Microsoft.Data.SqlClient.

Supporto per Always Encrypted Supporto per Always Encrypted con enclave sicura Framework di destinazione Versione di Microsoft.Data.SqlClient Sistema operativo
.NET Framework 4.6+ 1.1.0+ Windows
.NET Core 2.1+ 2.1.0+1 Windows, Linux, macOS
No .NET Standard 2.0 2.1.0+ Windows, Linux, macOS
.NET Standard 2.1+ 2.1.0+ Windows, Linux, macOS

Nota

1 Prima di Microsoft.Data.SqlClient versione 2.1.0, Always Encrypted era supportato solo in Windows.

Abilitazione di Always Encrypted per le query dell'applicazione

Il modo più semplice per abilitare la crittografia dei parametri e la decrittografia dei risultati delle query destinati alle colonne crittografate consiste nell'impostare il valore della parola chiave della stringa di connessione Column Encryption Setting su enabled.

L'esempio riportato di seguito usa una stringa di connessione che abilita Always Encrypted:

string connectionString = "Data Source=server63; Initial Catalog=Clinic; Integrated Security=true; Column Encryption Setting=enabled";
SqlConnection connection = new SqlConnection(connectionString);

Il frammento di codice riportato di seguito è un esempio equivalente che usa la proprietà SqlConnectionStringBuilder.ColumnEncryptionSetting.

SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
builder.DataSource = "server63";
builder.InitialCatalog = "Clinic";
builder.IntegratedSecurity = true;
builder.ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Enabled;
SqlConnection connection = new SqlConnection(builder.ConnectionString);
connection.Open();

Always Encrypted può anche essere abilitato per le singole query. Vedere la sezione Controllo dell'impatto di Always Encrypted sulle prestazioni di seguito. Perché la crittografia o la decrittografia abbiano esito positivo, non è sufficiente l'abilitazione di Always Encrypted. È necessario anche assicurarsi che:

  • L'applicazione abbia le autorizzazioni di database VIEW ANY COLUMN MASTER KEY DEFINITION e VIEW ANY COLUMN ENCRYPTION KEY DEFINITION , necessarie per accedere ai metadati sulle chiavi Always Encrypted nel database. Per informazioni dettagliate, vedere la sezione Autorizzazioni per il database in Always Encrypted (motore di database).
  • L'applicazione può accedere alla chiave master della colonna che protegge le chiavi di crittografia di colonna, le quali crittografano le colonne di database sottoposte a query.

Abilitazione di Always Encrypted con enclave sicuri

A partire da Microsoft.Data.SqlClient versione 1.1.0, il driver supporta Always Encrypted con enclave sicuri.

Per informazioni generali sullo sviluppo di applicazioni con enclave, vedere Sviluppare applicazioni tramite Always Encrypted con enclave sicure.

Per consentire i calcoli dell'enclave per una connessione di database, è necessario impostare le parole chiave della stringa di connessione seguenti, nonché abilitare Always Encrypted (come illustrato nella sezione precedente):

  • Attestation Protocol: specifica un protocollo di attestazione.

    • Se questa parola chiave non è specificata, le enclave sicure vengono disabilitati nella connessione.
    • Se si usa SQL Server con enclavi con sicurezza basata su virtualizzazione (VBS) e il servizio Sorveglianza host (HGS), il valore di questa parola chiave deve essere HGS.
    • Se si utilizza Azure SQL Database con le enclavi Intel SGX e l’attestazione di Microsoft Azure, il valore di questa parola chiave deve essere AAS.
    • Se si utilizza Azure SQL Database o SQL Server con enclave VBS e si vuole rinunciare all'attestazione, il valore di questa parola chiave deve essere None. Richiede la versione 4.1 o successiva.

    Nota

    “Nessuna” (nessuna attestazione) è l'unica opzione attualmente supportata per le enclave VBS in database SQL di Azure.

  • Enclave Attestation URL: specifica un URL di attestazione (endpoint di servizio di attestazione). È necessario ottenere un URL di attestazione per l'ambiente dall'amministratore del servizio di attestazione.

Per un'esercitazione dettagliata, vedere Esercitazione: Sviluppare un'applicazione .NET usando Always Encrypted con enclave sicuri.

Recupero e modifica dei dati nelle colonne crittografate

Dopo aver abilitato Always Encrypted per le query dell'applicazione, è possibile usare API SqlClient standard (vedere Recupero e modifica dei dati in ADO.NET) o le API del provider di dati Microsoft .NET per SQL Server, definite in Microsoft.Data.SqlClient Namespace, per recuperare o modificare dati nelle colonne di database crittografate. Se l'applicazione ha le autorizzazioni necessarie per il database e può accedere alla chiave master di colonna, il provider di dati Microsoft .NET per SQL Server eseguirà la crittografia dei parametri di query destinati a colonne crittografate ed eseguirà la decrittografia dei dati recuperati da colonne crittografate, restituendo valori in formato testo non crittografato di tipi .NET corrispondenti ai tipi di dati di SQL Server impostati per le colonne nello schema del database. Se Always Encrypted non è abilitato, le query con parametri destinati alle colonne crittografate avranno esito negativo. Le query possono comunque recuperare i dati dalle colonne crittografate, a condizione che non presentino parametri destinati alle colonne crittografate. Il provider di dati Microsoft .NET per SQL Server tuttavia non proverà a decrittografare tutti i valori recuperati dalle colonne crittografate e l'applicazione riceverà dati crittografati binari (come matrici di byte).

La tabella seguente riepiloga il comportamento delle query, a seconda che la funzionalità Always Encrypted sia abilitata o meno:

Caratteristica della query Always Encrypted è abilitato e l'applicazione può accedere alle chiavi e ai metadati delle chiavi Always Encrypted è abilitato e l'applicazione non può accedere alle chiavi o ai metadati delle chiavi Always Encrypted è disabilitato
Query con parametri destinati alle colonne crittografate. I valori dei parametri vengono crittografati in modo trasparente. Error Error
Query che recuperano dati dalle colonne crittografate, senza parametri destinati alle colonne crittografate. I risultati delle colonne vengono decrittografati in modo trasparente. L'applicazione riceve valori di testo non crittografato dei tipi di dati .NET corrispondenti ai tipi di SQL Server configurati per le colonne crittografate. Error I risultati delle colonne crittografate non vengono decrittografati. L'applicazione riceve i valori crittografati come matrici di byte (byte[]).

Gli esempi seguenti illustrano il recupero e la modifica dei dati nelle colonne crittografate. Negli esempi si presuppone la presenza della tabella di destinazione con lo schema seguente. Le colonne SSN e BirthDate sono crittografate.

CREATE TABLE [dbo].[Patients]([PatientId] [int] IDENTITY(1,1),
 [SSN] [char](11) COLLATE Latin1_General_BIN2
 ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC,
 ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256',
 COLUMN_ENCRYPTION_KEY = CEK1) NOT NULL,
 [FirstName] [nvarchar](50) NULL,
 [LastName] [nvarchar](50) NULL,
 [BirthDate] [date]
 ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED,
 ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256',
 COLUMN_ENCRYPTION_KEY = CEK1) NOT NULL
 PRIMARY KEY CLUSTERED ([PatientId] ASC) ON [PRIMARY])
 GO

Esempio di inserimento dei dati

Questo esempio illustra come inserire una riga nella tabella Patients. Osservare i seguenti dettagli:

  • Il codice di esempio non contiene alcun elemento specifico della crittografia. Il provider di dati Microsoft .NET per SQL Server rileva automaticamente e crittografa i parametri paramSSN e paramBirthdate destinati alle colonne crittografate. In questo modo la crittografia diventa trasparente per l'applicazione.
  • I valori inseriti nelle colonne di database, incluse quelle crittografate, vengono passati come oggetti SqlParameter . L'uso di SqlParameter è facoltativo quando si inviano i valori alle colonne non crittografate (è tuttavia consigliabile usarlo perché consente di impedire attacchi SQL injection), mentre è necessario per i valori destinati alle colonne crittografate. Se i valori inseriti nella colonna SSN o BirthDate sono stati passati come valori letterali incorporati nell'istruzione della query, la query non riuscirà perché il provider di dati Microsoft .NET per SQL Server non riesce a determinare i valori nelle colonne crittografate di destinazione e di conseguenza non ne esegue la crittografia. Di conseguenza, il server li rifiuterà come incompatibili con le colonne crittografate.
  • Il tipo di dati del parametro destinato alla colonna SSN è impostato su una stringa ANSI (non Unicode), che esegue il mapping al tipo di dati di SQL Server char/varchar. Se il tipo del parametro è stato impostato su una stringa Unicode (stringa), che esegue il mapping a nchar/nvarchar, la query avrà esito negativo perché Always Encrypted non supporta le conversioni da valori nchar/nvarchar crittografati a valori char/varchar crittografati. Vedere Mapping dei tipi di dati SQL Server per informazioni sui mapping dei tipi di dati.
  • Il tipo di dati del parametro inserito nella colonna BirthDate è impostato in modo esplicito sul tipo di dati SQL Server di destinazione tramite la proprietà SqlParameter.SqlDbType, anziché tramite il mapping implicito dei tipi .NET ai tipi di dati di SQL Server applicati quando si usa la proprietà SqlParameter.DbType. Per impostazione predefinita, viene eseguito il mapping della struttura DateTime al tipo di dati datetime di SQL Server. Poiché i dati della colonna BirthDate sono di tipo data e Always Encrypted non supporta una conversione di valori di data e ora crittografati in valori di data crittografati, l'uso del mapping predefinito restituirà un errore.
string connectionString = "Data Source=server63; Initial Catalog=Clinic; Integrated Security=true; Column Encryption Setting=enabled";

using (SqlConnection connection = new SqlConnection(builder.ConnectionString))
using (SqlCommand cmd = connection.CreateCommand())
{
    connection.Open();
    cmd.CommandText = @"INSERT INTO [dbo].[Patients] ([SSN], [FirstName], [LastName], [BirthDate]) VALUES (@SSN, @FirstName, @LastName, @BirthDate);";

    SqlParameter paramSSN = cmd.CreateParameter();
    paramSSN.ParameterName = @"@SSN";
    paramSSN.DbType = DbType.AnsiStringFixedLength;
    paramSSN.Direction = ParameterDirection.Input;
    paramSSN.Value = "795-73-9838";
    paramSSN.Size = 11;
    cmd.Parameters.Add(paramSSN);

    SqlParameter paramFirstName = cmd.CreateParameter();
    paramFirstName.ParameterName = @"@FirstName";
    paramFirstName.DbType = DbType.String;
    paramFirstName.Direction = ParameterDirection.Input;
    paramFirstName.Value = "Catherine";
    paramFirstName.Size = 50;
    cmd.Parameters.Add(paramFirstName);

    SqlParameter paramLastName = cmd.CreateParameter();
    paramLastName.ParameterName = @"@LastName";
    paramLastName.DbType = DbType.String;
    paramLastName.Direction = ParameterDirection.Input;
    paramLastName.Value = "Abel";
    paramLastName.Size = 50;
    cmd.Parameters.Add(paramLastName);

    SqlParameter paramBirthdate = cmd.CreateParameter();
    paramBirthdate.ParameterName = @"@BirthDate";
    paramBirthdate.SqlDbType = SqlDbType.Date;
    paramBirthdate.Direction = ParameterDirection.Input;
    paramBirthdate.Value = new DateTime(1996, 09, 10);
    cmd.Parameters.Add(paramBirthdate);

    cmd.ExecuteNonQuery();
}

Esempio di recupero di dati di testo non crittografato

L'esempio seguente illustra come filtrare i dati in base ai valori crittografati e recuperare i dati in testo non crittografato da colonne crittografate.

string connectionString = "Data Source=server63; Initial Catalog=Clinic; Integrated Security=true; Column Encryption Setting=enabled";
using (SqlConnection connection = new SqlConnection(builder.ConnectionString))
using (SqlCommand cmd = connection.CreateCommand())
{
    connection.Open();
    cmd.CommandText = @"SELECT [SSN], [FirstName], [LastName], [BirthDate] FROM [dbo].[Patients] WHERE SSN=@SSN";

    SqlParameter paramSSN = cmd.CreateParameter();
    paramSSN.ParameterName = @"@SSN";
    paramSSN.DbType = DbType.AnsiStringFixedLength;
    paramSSN.Direction = ParameterDirection.Input;
    paramSSN.Value = "795-73-9838";
    paramSSN.Size = 11;
    cmd.Parameters.Add(paramSSN);
    using (SqlDataReader reader = cmd.ExecuteReader())
    {
        if (reader.HasRows)
        {
            while (reader.Read())
            {
                Console.WriteLine(@"{0}, {1}, {2}, {3}", reader[0], reader[1], reader[2], ((DateTime)reader[3]).ToShortDateString());
            }
        }
    }
}

Nota

  • Il valore usato nella clausola WHERE per filtrare la colonna SSN deve essere passato usando SqlParameter, in modo che il provider di dati Microsoft .NET per SQL Server sia in grado di codificarli in modo trasparente prima dell'invio al database.

  • Tutti i valori stampati dal programma saranno in testo non crittografato, perché il provider di dati Microsoft .NET per SQL Server decrittografa in modo trasparente i dati recuperati dalle colonne SSN e BirthDate.

  • Le query possono eseguire confronti di uguaglianza nelle colonne se sono crittografate tramite crittografia deterministica.

Esempio di recupero di dati crittografati

Se Always Encrypted non è abilitato, una query può comunque recuperare dati dalle colonne crittografate, a condizione che non presenti parametri destinati alle colonne crittografate.

L'esempio seguente illustra come recuperare i dati crittografati binari dalle colonne crittografate.

string connectionString = "Data Source=server63; Initial Catalog=Clinic; Integrated Security=true";

using (SqlConnection connection = new SqlConnection(connectionString))
using (SqlCommand cmd = connection.CreateCommand())
{
    connection.Open();
    cmd.CommandText = @"SELECT [SSN], [FirstName], [LastName], [BirthDate] FROM [dbo].[Patients] WHERE [LastName]=@LastName";

    SqlParameter paramLastName = cmd.CreateParameter();
    paramLastName.ParameterName = @"@LastName";
    paramLastName.DbType = DbType.String;
    paramLastName.Direction = ParameterDirection.Input;
    paramLastName.Value = "Abel";
    paramLastName.Size = 50;
    cmd.Parameters.Add(paramLastName);
    using (SqlDataReader reader = cmd.ExecuteReader())
    {
        if (reader.HasRows)
        {
            while (reader.Read())
            {
                Console.WriteLine(@"{0}, {1}, {2}, {3}", BitConverter.ToString((byte[])reader[0]), reader[1], reader[2], BitConverter.ToString((byte[])reader[3]));
            }
        }
    }
}

Nota

  • Considerato che Always Encrypted non è abilitato nella stringa di connessione, la query restituirà i valori crittografati di SSN e BirthDate come matrici di byte (il programma converte i valori in stringhe).

  • Una query che recupera dati dalle colonne crittografate con Always Encrypted disabilitato può avere parametri, a condizione che nessuno dei parametri sia destinato a una colonna crittografata. La query precedente filtra in base alla colonna LastName, non è crittografata nel database. Se la query avesse filtrato per SSN o BirthDate, avrebbe avuto esito negativo.

Come evitare i problemi comuni quando si eseguono query su colonne crittografate

Questa sezione descrive le categorie di errori che si verificano con maggiore frequenza quando si eseguono query su colonne crittografate da applicazioni .NET e fornisce alcune linee guida su come evitare il problema.

Errori di conversione dei tipi di dati non supportati

Always Encrypted supporta alcune conversioni per i tipi di dati crittografati. Per l'elenco dettagliato delle conversioni di tipi supportate, vedere Always Encrypted. Per evitare errori di conversione dei tipi di dati, eseguire le operazioni seguenti:

  • Impostare i tipi di parametri destinati alle colonne crittografate in modo che il tipo di dati di SQL Server del parametro corrisponda esattamente al tipo della colonna di destinazione oppure che sia supportata una conversione del tipo di dati di SQL Server del parametro nel tipo di destinazione della colonna. È possibile applicare il mapping desiderato dei tipi di dati .NET a specifici tipi di dati di SQL Server usando la proprietà SqlParameter.SqlDbType.
  • Verificare che la precisione e la scala dei parametri destinati alle colonne dei tipi di dati di SQL Server decimali e numerici siano uguali a quelle configurate per la colonna di destinazione.
  • Verificare che la precisione dei parametri destinati a colonne con tipi di dati di SQL Server datetime2, datetimeoffset o time non sia maggiore della precisione per la colonna di destinazione (nelle query che modificano i valori di questa colonna).

Errori causati dal passaggio di testo non crittografato anziché di valori crittografati

Qualsiasi valore destinato a una colonna crittografata deve essere crittografato all'interno dell'applicazione. Il tentativo di inserire, modificare o filtrare in base a un valore in formato testo non crittografato in una colonna crittografata restituirà un errore simile al seguente:

Microsoft.Data.SqlClient.SqlException (0x80131904): Operand type clash: varchar is incompatible with varchar(8000) encrypted with (encryption_type = 'DETERMINISTIC', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'CEK_Auto1', column_encryption_key_database_name = 'Clinic') collation_name = 'SQL_Latin1_General_CP1_CI_AS'

Per evitare tali errori, verificare che:

  • Always Encrypted sia abilitato per le query dell'applicazione destinate alle colonne crittografate (per la stringa di connessione o nell'oggetto SqlCommand per una query specifica).
  • Per inviare i dati destinati alle colonne crittografate venga usato SqlParameter. L'esempio seguente illustra una query che filtra in modo errato in base a un valore letterale o costante in una colonna crittografata (SSN) anziché passare il valore letterale all'interno di un oggetto SqlParameter.
using (SqlCommand cmd = connection.CreateCommand())
{
    cmd.CommandText = @"SELECT [SSN], [FirstName], [LastName], [BirthDate] FROM [dbo].[Patients] WHERE SSN = '795-73-9838'";
    cmd.ExecuteNonQuery();
}

Uso degli archivi delle chiavi master delle colonne

Per crittografare un valore di parametro o decrittografare i dati nei risultati della query, il provider di dati Microsoft .NET per SQL Server deve ottenere una chiave di crittografia di colonna che è configurata per la colonna di destinazione. Le chiavi di crittografia di colonna vengono archiviate in forma crittografata nei metadati del database. Ogni chiave di crittografia di colonna ha una chiave master corrispondente che è stata usata per crittografare la chiave di crittografia. I metadati del database non archiviano le chiavi master di colonna, ma includono solo le informazioni su un archivio chiavi contenente una chiave master di colonna specifica e il percorso della chiave nell'archivio chiavi.

Per ottenere un valore di testo non crittografato di una chiave di crittografia della colonna, il provider di dati Microsoft .NET per SQL Server ottiene prima i metadati relativi sia alla chiave di crittografia della colonna che alla chiave master della colonna corrispondente. Quindi, usa le informazioni dei metadati per contattare l'archivio chiavi contenente la chiave master della colonna e decrittografare la chiave di crittografia della colonna crittografata. Il provider di dati Microsoft .NET per SQL Server comunica con un archivio delle chiavi usando un provider dell'archivio chiavi master delle colonne, ovvero un'istanza di una classe derivata dalla classe SqlColumnEncryptionKeyStoreProvider.

Il processo per ottenere la chiave di crittografia di una colonna consiste nelle fasi seguenti:

  1. Se Always Encrypted è abilitato per una query, il provider di dati Microsoft .NET per SQL Server chiama sys.sp_describe_parameter_encryption in modo trasparente per recuperare i metadati di crittografia per i parametri destinati alle colonne crittografate, se la query include parametri. Per i dati crittografati contenuti nei risultati di una query, SQL Server associa automaticamente i metadati di crittografia. Le informazioni sulla chiave master della colonna includono:

    • Il nome di un provider di archivio delle chiavi che incapsula un archivio contenente la chiave master della colonna.
    • Il percorso che specifica la posizione della chiave master della colonna nell'archivio delle chiavi.

    Le informazioni sulla chiave di crittografia della colonna includono:

    • Il valore crittografato della chiave di crittografia della colonna.
    • Il nome dell'algoritmo usato per crittografare la chiave di crittografia della colonna.
  2. Il provider di dati Microsoft .NET per SQL Server usa il nome del provider dell'archivio chiavi master delle colonne per cercare l'oggetto provider, ovvero un'istanza di una classe derivata dalla classe SqlColumnEncryptionKeyStoreProvider in una struttura di dati interna.

  3. Per decrittografare la chiave di crittografia della colonna, il provider di dati Microsoft .NET per SQL Server chiama il metodo SqlColumnEncryptionKeyStoreProvider.DecryptColumnEncryptionKey() passando il percorso della chiave master della colonna, il valore crittografato della chiave di crittografia della colonna e il nome dell'algoritmo di crittografia usato per generare la chiave di crittografia della colonna crittografata.

Uso dei provider predefiniti di archivio delle chiavi master delle colonne

Il provider di dati Microsoft .NET per SQL Server include i seguenti provider predefiniti dell'archivio chiavi master delle colonne, che sono preregistrati con i nomi di provider specifici (usati per cercare il provider). Questi provider di archivi di chiavi predefiniti sono supportati solo in Windows.

Classe Descrizione Nome del provider (ricerca) Piattaforma
Classe SqlColumnEncryptionCertificateStoreProvider Un provider per l'archivio certificati Windows. MSSQL_CERTIFICATE_STORE Finestre
Classe SqlColumnEncryptionCngProvider Un provider di archivio delle chiavi che supporta Microsoft Cryptography API: Next Generation (CNG). In genere, un archivio di questo tipo è un modulo di protezione hardware, ovvero un dispositivo fisico che protegge e gestisce le chiavi digitali e fornisce l'elaborazione della crittografia. MSSQL_CNG_STORE Finestre
Classe SqlColumnEncryptionCspProvider Un provider di un archivio delle chiavi che supporta Microsoft CryptoAPI (CAPI). In genere, un archivio di questo tipo è un modulo di protezione hardware, ovvero un dispositivo fisico che protegge e gestisce le chiavi digitali e fornisce l'elaborazione della crittografia. MSSQL_CSP_PROVIDER Finestre

Per usare questi provider, non è necessario apportare alcuna modifica al codice dell'applicazione, ma tenere presente i dettagli seguenti:

  • È necessario che l'utente (o l'amministratore del database) verifichi che il nome del provider, configurato nei metadati della chiave master della colonna, sia corretto e che il percorso della chiave master sia conforme al formato del percorso della chiave valido per un determinato provider. È consigliabile configurare le chiavi usando strumenti come SQL Server Management Studio, che genera automaticamente nomi di provider e percorsi di chiave validi quando viene eseguita l'istruzione CREATE COLUMN MASTER KEY (Transact-SQL). Per altre informazioni, vedere Configurare Always Encrypted usando SQL Server Management Studio e Configurare Always Encrypted tramite PowerShell.
  • Verificare che l'applicazione possa accedere alla chiave nell'archivio chiavi. Questo processo può comportare l'accesso da parte dell'applicazione alla chiave e/o all'archivio chiavi, a seconda dell'archivio chiavi, o l'esecuzione di altri passaggi di configurazione specifici dell'archivio chiavi. Ad esempio, per accedere a un archivio chiavi che implementa CNG o CAPI (come un modulo di protezione hardware), è necessario assicurarsi che nel computer dell'applicazione sia installata una libreria di implementazione di CNG o CAPI per l'archivio. Per informazioni dettagliate, vedere Creare e archiviare chiavi master della colonna per Always Encrypted.

Uso del provider Azure Key Vault

L'insieme di credenziali delle chiavi di Azure rappresenta una scelta valida per archiviare e gestire le chiavi master delle colonne per Always Encrypted, soprattutto se le applicazioni sono ospitate in Azure. Il provider di dati Microsoft .NET per SQL Server non include un provider dell'archivio chiavi master di colonna per Azure Key Vault, ma è disponibile come pacchetto NuGet (Microsoft.Data.SqLClient.AlwaysEncrypted.AzureKeyVaultProvider), che può essere facilmente integrato con l'applicazione. Per informazioni dettagliate, vedere Always Encrypted: Proteggere i dati sensibili nel database SQL con la crittografia dei dati e archiviare le chiavi di crittografia nell'insieme di credenziali delle chiavi di Azure.

Classe Descrizione Nome del provider (ricerca) Piattaforma
Classe SqlColumnEncryptionAzureKeyVaultProvider Provider per Azure Key Vault. AZURE_KEY_VAULT Windows, Linux, macOS

Supporto di .NET

Versione Versione di Microsoft.Data.SqlClient Piattaforme .NET
3.0.0 3.0.0+ .NET Framework 4.6.1+, .NET Core 2.1+, .NET Standard 2.0+
2.0.0 1.1.3+
2.1.0+
.NET Framework 4.6.1+, .NET Core 2.1+
.NET Standard 2.0+
1.2.0 1.0.19269.1+
2.1.0+
.NET Framework 4.6+, .NET Core 2.1+
.NET Standard 2.0+
1.1.0 1.0.19269.1+ .NET Framework 4.6+, .NET Core 2.1+
1.0.0 1.0.19269.1+ .NET Framework 4.6+, .NET Core 2.1+

A partire dalla versione 3.0.0, Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider supporta le funzionalità di memorizzazione nella cache delle chiavi di crittografia di colonna durante la registrazione del provider usando l'API SqlConnection.RegisterColumnEncryptionKeyStoreProvidersOnConnection o SqlCommand.RegisterColumnEncryptionKeyStoreProvidersOnCommand.

A partire dalla versione 2.0.0, Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider supporta le nuove API Azure.Core e Azure.Identity per eseguire l'autenticazione con Azure Key Vault. È ora possibile passare un'istanza dell'implementazione di TokenCredential a costruttori SqlColumnEncryptionAzureKeyVaultProvider per inizializzare l'oggetto provider di Azure Key Vault.

Nota

Microsoft.Data.SqLClient.AlwaysEncrypted.AzureKeyVaultProvider supporta insiemi di credenziali e moduli di protezione hardware gestiti in Azure Key Vault.

Per alcuni esempi che mostrano la crittografia/decrittografia con Azure Key Vault, vedere Funzionamento di Azure Key Vault con Always Encrypted e Funzionamento di Azure Key Vault con Always Encrypted con enclave sicure.

Implementazione di un provider personalizzato di archivio delle chiavi master delle colonne

Se si vogliono archiviare chiavi master di colonna in un archivio chiavi non supportato da un provider esistente, è possibile implementare un provider personalizzato estendendo la classe SqlColumnEncryptionKeyStoreProvider e registrando il provider con uno dei metodi seguenti:

public class MyCustomKeyStoreProvider : SqlColumnEncryptionKeyStoreProvider
{
    public const string ProviderName = "MY_CUSTOM_STORE";

    public override byte[] EncryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] columnEncryptionKey)
    {
        // Logic for encrypting a column encrypted key.
    }
    public override byte[] DecryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] EncryptedColumnEncryptionKey)
    {
        // Logic for decrypting a column encrypted key.
    }
}  
class Program
{
    static void Main(string[] args)
    {
        Dictionary<string, SqlColumnEncryptionKeyStoreProvider> providers =
            new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>();
        providers.Add(MyCustomKeyStoreProvider.ProviderName, new MyCustomKeyStoreProvider());
        SqlConnection.RegisterColumnEncryptionKeyStoreProviders(providers);
        // ...
    }
}

Precedenza della cache della chiave di crittografia della colonna

Questa sezione si applica alla versione 3.0 e successive del provider di dati Microsoft .NET per SQL Server.

Le chiavi di crittografia di colonna decrittografate da provider di archivi chiavi personalizzati registrati in un'istanza di connessione o di comando non verranno memorizzate nella cache dal provider di dati Microsoft .NET per SQL Server. I provider di archivi chiavi personalizzati devono implementare il proprio meccanismo di memorizzazione nella cache delle chiavi di crittografia di colonna.

A partire dalla versione 3.0.0 di Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider, ogni istanza di Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider ha la propria implementazione di memorizzazione nella cache per le chiavi di crittografia di colonna. Se registrate in un'istanza di connessione o di comando, le chiavi di crittografia di colonna decrittografate da un'istanza di Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider verranno cancellate quando l'istanza non rientra più nell'ambito:

class Program
{
    static void Main()
    {
        using (SqlConnection connection = new SqlConnection(connectionString))
        {
            using (SqlCommand command = connection.CreateCommand())
            {
                Dictionary<string, SqlColumnEncryptionKeyStoreProvider> customKeyStoreProviders = new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>();
                SqlColumnEncryptionAzureKeyVaultProvider azureKeyVaultProvider = new SqlColumnEncryptionAzureKeyVaultProvider();
                customKeyStoreProviders.Add(SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, azureKeyVaultProvider);
                command.RegisterColumnEncryptionKeyStoreProvidersOnCommand(customKeyStoreProviders);
                // Perform database operation using Azure Key Vault Provider
                // Any decrypted column encryption keys will be cached
            } // Column encryption key cache of "azureKeyVaultProvider" is cleared when "azureKeyVaultProvider" goes out of scope
        }
    }
}

Nota

La memorizzazione nella cache delle chiavi di crittografia di colonna implementata da provider di archivi chiavi personalizzati verrà disabilitata dal driver se l'istanza del provider dell'archivio chiavi viene registrata nel driver a livello globale usando il metodo SqlConnection.RegisterColumnEncryptionKeyStoreProviders. Qualsiasi implementazione della memorizzazione nella cache delle chiavi di crittografia di colonna deve fare riferimento al valore di SqlColumnEncryptionKeyStoreProvider.ColumnEncryptionKeyCacheTtl prima di memorizzare nella cache una chiave di crittografia di colonna e non deve eseguire la memorizzazione nella cache della chiave se il valore è zero. In questo modo, si evitano casi di memorizzazione nella cache duplicati e la possibile confusione degli utenti quando tentano di configurare la memorizzazione nella cache delle chiavi.

Registrazione di un provider dell'archivio chiavi master di colonna personalizzato

Questa sezione si applica alla versione 3.0 e successive del provider.

I provider di archivi chiavi master personalizzati possono essere registrati con il driver a tre livelli diversi. Di seguito viene indicata la precedenza delle tre registrazioni:

  • Viene verificata la registrazione per comando, se non è vuota.
  • Se la registrazione per comando è vuota, viene verificata la registrazione per connessione, se non è vuota.
  • Se la registrazione per connessione è vuota, viene verificata la registrazione globale.

Dopo aver individuato un provider dell'archivio chiavi a livello di registrazione, il driver NON eseguirà il fallback alle altre registrazioni per cercare un provider. Se i provider sono registrati, ma il provider appropriato non viene trovato a un determinato livello, verrà generata un'eccezione contenente solo i provider registrati nella registrazione verificata.

I provider di archivi chiavi master di colonna predefiniti disponibili per l'archivio certificati di Windows, l'archivio CNG e CSP sono preregistrati.

I tre livelli di registrazione supportano scenari diversi quando si eseguono query su dati crittografati. È possibile usare il metodo appropriato per garantire che un utente di un'applicazione possa accedere ai dati in formato testo non crittografato se è in grado di fornire la chiave master di colonna richiesta, eseguendo l'autenticazione nell'archivio chiavi che contiene la chiave master di colonna.

Le applicazioni che condividono un'istanza di SqlConnection tra più utenti potrebbero scegliere di usare SqlCommand.RegisterColumnEncryptionKeyStoreProvidersOnCommand. Ogni utente deve registrare un provider dell'archivio chiavi in un'istanza di SqlCommand prima di eseguire una query per accedere a una colonna crittografata. Se il provider dell'archivio chiavi è in grado di accedere alla chiave master di colonna richiesta nell'archivio chiavi usando le credenziali specificate dell'utente, la query riuscirà.

Le applicazioni che creano un'istanza di SqlConnection per ogni utente potrebbero scegliere di usare SqlConnection.RegisterColumnEncryptionKeyStoreProvidersOnConnection. I provider di archivi chiavi registrati con questo metodo possono essere usati dalla connessione per qualsiasi query che accede ai dati crittografati.

I provider di archivi chiavi registrati con SqlConnection.RegisterColumnEncryptionKeyStoreProviders useranno l'identità specificata dall'applicazione durante l'autenticazione nell'archivio chiavi.

L'esempio seguente mostra la precedenza dei provider di archivi chiavi master di colonna personalizzati registrati in un'istanza di connessione:

class Program
{
    static void Main()
    {
        Dictionary<string, SqlColumnEncryptionKeyStoreProvider> customKeyStoreProviders = new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>();
        MyCustomKeyStoreProvider myProvider = new MyCustomKeyStoreProvider();
        customKeyStoreProviders.Add("MY_CUSTOM_STORE", myProvider);
        // Registers the provider globally
        SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customKeyStoreProviders);

        using (SqlConnection connection = new SqlConnection(connectionString))
        {
            customKeyStoreProviders.Clear();
            SqlColumnEncryptionAzureKeyVaultProvider azureKeyVaultProvider = new SqlColumnEncryptionAzureKeyVaultProvider();
            customKeyStoreProviders.Add(SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, azureKeyVaultProvider);
            // Registers the provider on the connection
            // These providers will take precedence over globally registered providers
            connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(customKeyStoreProviders);
        }
    }
}

L'esempio seguente mostra la precedenza dei provider di archivi chiavi master di colonna personalizzati registrati in un'istanza di comando:

class Program
{
    static void Main()
    {
        Dictionary<string, SqlColumnEncryptionKeyStoreProvider> customKeyStoreProviders = new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>();
        MyCustomKeyStoreProvider firstProvider = new MyCustomKeyStoreProvider();
        customKeyStoreProviders.Add("FIRST_CUSTOM_STORE", firstProvider);
        // Registers the provider globally
        SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customKeyStoreProviders);

        using (SqlConnection connection = new SqlConnection(connectionString))
        {
            customKeyStoreProviders.Clear();
            MyCustomKeyStoreProvider secondProvider = new MyCustomKeyStoreProvider();
            customKeyStoreProviders.Add("SECOND_CUSTOM_STORE", secondProvider);
            // Registers the provider on the connection
            connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(customKeyStoreProviders);

            using (SqlCommand command = connection.CreateCommand())
            {
                customKeyStoreProviders.Clear();
                SqlColumnEncryptionAzureKeyVaultProvider azureKeyVaultProvider = new SqlColumnEncryptionAzureKeyVaultProvider();
                customKeyStoreProviders.Add(SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, azureKeyVaultProvider);
                // Registers the provider on the command
                // These providers will take precedence over connection-level providers and globally registered providers
                command.RegisterColumnEncryptionKeyStoreProvidersOnCommand(customKeyStoreProviders);
            }
        }
    }
}

Uso di provider di archivio delle chiavi master delle colonne per il provisioning di chiavi a livello di codice

Quando il provider di dati Microsoft .NET per SQL Server accede a colonne crittografate, trova e chiama in modo trasparente il provider dell'archivio chiavi master di colonna corretto per decrittografare le chiavi di crittografia di colonna. In genere, il codice dell'applicazione normale non chiama direttamente i provider di archivio delle chiavi master delle colonne. Tuttavia, è possibile creare un'istanza e chiamare un provider in modo esplicito per creare e gestire a livello di codice chiavi Always Encrypted: per generare una chiave di crittografia di colonna crittografata e decrittografare una chiave di crittografia di colonna (ad esempio nell'ambito della rotazione delle chiavi master di colonna). Per altre informazioni, vedere Panoramica della gestione delle chiavi per Always Encrypted. L'implementazione di strumenti di gestione delle chiavi personali può essere necessaria solo se si usa un provider di archivio delle chiavi personalizzato. Quando si usano chiavi archiviate in archivi chiavi, per cui esistono provider predefiniti, e/o in Azure Key Vault, è possibile usare strumenti esistenti, ad esempio SQL Server Management Studio o PowerShell, per gestire le chiavi ed effettuarne il provisioning. L'esempio seguente illustra la generazione della chiave di crittografia di una colonna e l'uso della classe SqlColumnEncryptionCertificateStoreProvider per crittografare la chiave con un certificato.

using System.Security.Cryptography;
static void Main(string[] args)
{
    byte[] EncryptedColumnEncryptionKey = GetEncryptedColumnEncryptonKey();
    Console.WriteLine("0x" + BitConverter.ToString(EncryptedColumnEncryptionKey).Replace("-", ""));
    Console.ReadKey();
}

static byte[]  GetEncryptedColumnEncryptonKey()
{
    int cekLength = 32;
    String certificateStoreLocation = "CurrentUser";
    String certificateThumbprint = "698C7F8E21B2158E9AED4978ADB147CF66574180";
    // Generate the plaintext column encryption key.
    byte[] columnEncryptionKey = new byte[cekLength];
    RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider();
    rngCsp.GetBytes(columnEncryptionKey);

    // Encrypt the column encryption key with a certificate.
    string keyPath = String.Format(@"{0}/My/{1}", certificateStoreLocation, certificateThumbprint);
    SqlColumnEncryptionCertificateStoreProvider provider = new SqlColumnEncryptionCertificateStoreProvider();
    return provider.EncryptColumnEncryptionKey(keyPath, @"RSA_OAEP", columnEncryptionKey);
}

Controllo dell'impatto di Always Encrypted sulle prestazioni

Dato che Always Encrypted è una tecnologia di crittografia lato client, la maggior parte del sovraccarico delle prestazioni si verifica sul lato client, non nel database. A parte il costo delle operazioni di crittografia e decrittografia, le altre origini di sovraccarico delle prestazioni sul lato client sono le seguenti:

  • Round trip aggiuntivi al database per recuperare metadati per i parametri di query.
  • Chiamate a un archivio delle chiavi master delle colonne per accedere alla chiave master di una colonna.

Questa sezione descrive le ottimizzazioni delle prestazioni predefinite nel provider di dati Microsoft .NET per SQL Server e come controllare l'impatto sulle prestazioni esercitato dai due fattori descritti.

Controllo dei round trip per recuperare i metadati per i parametri di query

Se Always Encrypted è abilitato per una connessione, per impostazione predefinita il provider di dati Microsoft .NET per SQL Server chiamerà sys.sp_describe_parameter_encryption per ogni query con parametri, passando l'istruzione di query (senza i valori dei parametri) a SQL Server. sys.sp_describe_parameter_encryption analizza l'istruzione di query per verificare se sono presenti parametri da crittografare e, in tal caso, restituisce le informazioni relative alla crittografia che consentiranno al provider di dati Microsoft .NET per SQL Server di crittografare i valori dei parametri. Il comportamento descritto garantisce all'applicazione client un elevato livello di trasparenza. Non è necessario che l'applicazione (e lo sviluppatore dell'applicazione) sappiano quali query accedono alle colonne crittografate, a condizione che i valori destinati alle colonne crittografate vengano passati al provider di dati Microsoft .NET per SQL Server negli oggetti SqlParameter.

Memorizzazione nella cache dei metadati di query

Il provider di dati Microsoft .NET per SQL Server memorizza nella cache i risultati di sys.sp_describe_parameter_encryption per ogni istruzione di query. Di conseguenza, se la stessa istruzione di query viene eseguita più volte, il driver chiama sys.sp_describe_parameter_encryption solo una volta. La memorizzazione nella cache dei metadati di crittografia per le istruzioni di query consente di ridurre in modo sostanziale l'impatto sulle prestazioni del recupero dei metadati dal database. La memorizzazione nella cache è abilitata per impostazione predefinita. È possibile disabilitare la memorizzazione nella cache dei metadati dei parametri impostando la proprietà SqlConnection.ColumnEncryptionQueryMetadataCacheEnabled su false, ma questa operazione non è consigliabile tranne in rari casi, come quello descritto di seguito:

Si consideri un database che ha due schemi diversi: s1 e s2. Ogni schema contiene una tabella con lo stesso nome: t. Le definizioni delle tabelle s1.t e s2.t sono identiche, ad eccezione delle proprietà relative alla crittografia: una colonna, denominata c, in s1.t non è crittografata, ma è crittografata in s2.t. Il database ha due utenti: u1 e u2. Lo schema predefinito per l'utente u1 è s1. Lo schema predefinito per u2 è s2. Un'applicazione .NET apre due connessioni al database, rappresentando l'utente u1 su una connessione e l'utente u2 su un'altra connessione. L'applicazione invia una query con un parametro destinato alla colonna c tramite la connessione per l'utente u1. Poiché la query non specifica lo schema, si presuppone che venga usato lo schema utente predefinito. Successivamente, l'applicazione invia la stessa query tramite la connessione per l'utente u2. Se la memorizzazione nella cache dei metadati delle query è abilitata, dopo la prima query la cache verrà popolata con metadati indicanti che la colonna c, che è la destinazione del parametro di query, non è crittografata. Poiché la seconda query ha la stessa istruzione di query, verranno usate le informazioni memorizzate nella cache. Di conseguenza, il driver invierà la query senza crittografare il parametro (anche se questo non è corretto perché la colonna di destinazione, s2.t.c, è crittografata), passando al server il valore di testo non crittografato del parametro. Il server rileverà l'incompatibilità e forzerà l'aggiornamento della cache. Di conseguenza, l'applicazione invierà nuovamente la query in modo trasparente con il valore del parametro correttamente crittografato. In tal caso, la memorizzazione nella cache deve essere disabilitata per impedire che valori sensibili vengano passati al server in modalità non crittografata.

Impostazione di Always Encrypted a livello di query

Per controllare l'impatto sulle prestazioni del recupero dei metadati di crittografia per le query con parametri, è possibile abilitare Always Encrypted per singole query anziché l'impostare la funzionalità per la connessione. In questo modo, è possibile assicurarsi che sys.sp_describe_parameter_encryption venga richiamato solo per le query con parametri destinati alle colonne crittografate. Si noti tuttavia che in questo modo si riduce la trasparenza della crittografia. Se si modificano le proprietà di crittografia delle colonne di database, potrebbe essere necessario modificare il codice dell'applicazione per allinearlo alle modifiche dello schema.

Nota

L'impostazione di Always Encrypted a livello delle singole query limita i vantaggio in termini di prestazioni della memorizzazione nella cache dei metadati di crittografia dei parametri.

Per controllare il comportamento di Always Encrypted per le singole query, è necessario usare questo costruttore di SqlCommand e SqlCommandColumnEncryptionSetting. Di seguito sono riportate alcune linee guida utili:

  • Se la maggior parte delle query eseguite da un'applicazione client accede a colonne crittografate:
    • Impostare la parola chiave della stringa di connessione di Impostazione di crittografia di colonna su Abilitata.
    • Impostare SqlCommandColumnEncryptionSetting su Disabled per singole query che non accedono ad alcuna colonna crittografata. Questa impostazione disabilita sia la chiamata di sys.sp_describe_parameter_encryption sia un tentativo di decrittografia di valori nel set di risultati.
    • Impostare SqlCommandColumnEncryptionSetting su ResultSetOnly per singole query che non includono parametri che richiedono la crittografia, ma recuperano dati da colonne crittografate. Questa impostazione disabilita la chiamata di sys.sp_describe_parameter_encryption e la crittografia dei parametri. La query potrà decrittografare i risultati delle colonne di crittografia.
  • Se la maggior parte delle query eseguite da un'applicazione client non accede a colonne crittografate:
    • Impostare la parola chiave della stringa di connessione di Impostazione di crittografia di colonna su Disabilitata.
    • Impostare SqlCommandColumnEncryptionSetting su Enabled per singole query che non includono parametri che devono essere crittografati. Questa impostazione abilita sia la chiamata di sys.sp_describe_parameter_encryption sia la decrittografia dei risultati di query recuperati da colonne crittografate.
    • Impostare SqlCommandColumnEncryptionSetting su ResultSetOnly per le query che non includono parametri che richiedono la crittografia, ma recuperano dati da colonne crittografate. Questa impostazione disabilita la chiamata di sys.sp_describe_parameter_encryption e la crittografia dei parametri. La query potrà decrittografare i risultati delle colonne di crittografia.

Nell'esempio seguente Always Encrypted è disabilitato per la connessione al database. La query eseguita dall'applicazione ha un parametro la cui destinazione è la colonna LastName non crittografata. La query recupera i dati dalle colonne SSN e BirthDate, che sono entrambe crittografate. In questo caso, la chiamata di sys.sp_describe_parameter_encryption per recuperare i metadati di crittografia non è necessaria. È tuttavia necessario abilitare la decrittografia dei risultati della query in modo che l'applicazione possa ricevere i valori di testo non crittografato dalle due colonne crittografate. A questo scopo, è necessario usare l'impostazione ResultSetOnly per SqlCommandColumnEncryptionSetting.

string connectionString = "Data Source=server63; Initial Catalog=Clinic; Integrated Security=true";
using (SqlConnection connection = new SqlConnection(connectionString))
using (SqlCommand cmd = new SqlCommand(@"SELECT [SSN], [FirstName], [LastName], [BirthDate] FROM [dbo].[Patients] WHERE [LastName]=@LastName",
connection, null, SqlCommandColumnEncryptionSetting.ResultSetOnly))
{
    connection.Open();
    SqlParameter paramLastName = cmd.CreateParameter();
    paramLastName.ParameterName = @"@LastName";
    paramLastName.DbType = DbType.String;
    paramLastName.Direction = ParameterDirection.Input;
    paramLastName.Value = "Abel";
    paramLastName.Size = 50;
    cmd.Parameters.Add(paramLastName);
    using (SqlDataReader reader = cmd.ExecuteReader())
    {
        if (reader.HasRows)
        {
            while (reader.Read())
            {
                Console.WriteLine(@"{0}, {1}, {2}, {3}", reader[0], reader[1], reader[2], ((DateTime)reader[3]).ToShortDateString());
            }
        }
    }
}

Memorizzazione nella cache delle chiavi di crittografia della colonna

Per ridurre il numero di chiamate a un archivio chiavi master della colonna necessarie per decrittografare le chiavi di crittografia della colonna, il provider di dati Microsoft .NET per SQL Server memorizza nella cache le chiavi di crittografia della colonna di testo non crittografato. Dopo aver ricevuto il valore della chiave di crittografia di colonna crittografata dai metadati del database, il driver prova prima di tutto a trovare la chiave di crittografia di colonna in formato testo non crittografato corrispondente al valore della chiave crittografata. Il driver chiama l'archivio chiavi contenente la chiave master di colonna solo se non trova nella cache il valore della chiave di crittografia di colonna crittografata.

Le voci della cache vengono rimosse per motivi di sicurezza dopo un intervallo di durata (TTL) configurabile. Il valore predefinito di questo intervallo è 2 ore. Se si preferisce impostare requisiti di sicurezza più severi relativi al tempo per cui le chiavi di crittografia delle colonne possono rimanere memorizzate nella cache in testo non crittografato nell'applicazione, è possibile modificare il valore della durata (TTL) tramite la proprietà SqlConnection.ColumnEncryptionKeyCacheTtl.

Per i provider di archivi chiavi personalizzati registrati con SqlConnection.RegisterColumnEncryptionKeyStoreProvidersOnConnection e SqlCommand.RegisterColumnEncryptionKeyStoreProvidersOnCommand, le chiavi di crittografia di colonna decrittografate non verranno memorizzate nella cache dal provider di dati Microsoft .NET per SQL Server. I provider di archivi chiavi personalizzati devono invece implementare il proprio meccanismo di memorizzazione nella cache. La Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProviderversione 3.0.0 e successive include un'implementazione propria della memorizzazione nella cache.

Per supportare scenari in cui diversi utenti della stessa applicazione possono eseguire più query, i provider di archivi chiavi personalizzati possono essere mappati a un utente e registrati in un'istanza di connessione o di comando specifica per l'utente. L'esempio seguente mostra in che modo è possibile riutilizzare un'istanza di Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider tra oggetti SqlCommand diversi per lo stesso utente. La cache delle chiavi di crittografia di colonna persiste tra più query, riducendo il numero di round trip nell'archivio chiavi:

using Microsoft.Data.SqlClient;
using Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider;
using System.Collections.Generic;

class Program
{
    // Maps a SqlColumnEncryptionAzureKeyVaultProvider to some object that represents a user
    static Dictionary<object, SqlColumnEncryptionAzureKeyVaultProvider> providerByUser = new();

    void ExecuteSelectQuery(object user, SqlConnection connection)
    {
        // Check if the user already has a SqlColumnEncryptionAzureKeyVaultProvider
        SqlColumnEncryptionAzureKeyVaultProvider azureKeyVaultProvider = providerByUser[user];
        if (azureKeyVaultProvider is null)
        {
            // Create a new SqlColumnEncryptionAzureKeyVaultProvider with the user's credentials and save it for future use
            azureKeyVaultProvider = new SqlColumnEncryptionAzureKeyVaultProvider();
            providerByUser[user] = azureKeyVaultProvider;
        }

        Dictionary<string, SqlColumnEncryptionKeyStoreProvider> customProviders = new();
        customProviders.Add(SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, azureKeyVaultProvider);

        using SqlCommand command = new("SELECT * FROM Customers", connection);
        command.RegisterColumnEncryptionKeyStoreProvidersOnCommand(customProviders);
        // Perform database operations
        // Any decrypted column encryption keys will be cached by azureKeyVaultProvider
    }

    void ExecuteUpdateQuery(object user, SqlConnection connection)
    {
        // Check if the user already has a SqlColumnEncryptionAzureKeyVaultProvider
        SqlColumnEncryptionAzureKeyVaultProvider azureKeyVaultProvider = providerByUser[user];
        if (azureKeyVaultProvider is null)
        {
            // Create a new SqlColumnEncryptionAzureKeyVaultProvider with the user's credentials and save it for future use
            azureKeyVaultProvider = new SqlColumnEncryptionAzureKeyVaultProvider();
            providerByUser[user] = azureKeyVaultProvider;
        }

        Dictionary<string, SqlColumnEncryptionKeyStoreProvider> customProviders = new();
        customProviders.Add(SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, azureKeyVaultProvider);

        using SqlCommand command = new("UPDATE Customers SET Name = 'NewName' WHERE CustomerId = 1", connection);
        command.RegisterColumnEncryptionKeyStoreProvidersOnCommand(customProviders);
        // Perform database operations
        // Any decrypted column encryption keys will be cached by azureKeyVaultProvider
    }
}

Abilitazione di protezione aggiuntiva per un'istanza di SQL Server compromessa

Per impostazione predefinita, il provider di dati Microsoft .NET per SQL Server si basa sul sistema di database (SQL Server o database SQL di Azure) per rendere disponibili metadati sulle colonne del database crittografate e sulla modalità di crittografia usata. I metadati di crittografia consentono al provider di dati Microsoft .NET per SQL Server di crittografare i parametri di query e decrittografare i risultati di query senza alcun intervento da parte dell'applicazione, riducendo così notevolmente il numero di modifiche necessarie nell'applicazione. Se tuttavia il processo di SQL Server è compromesso e un utente malintenzionato manomette i metadati inviati da SQL Server al provider di dati Microsoft .NET per SQL Server, tale utente potrebbe essere in grado di sottrarre informazioni sensibili. Questa sezione descrive le API che consentono di fornire un ulteriore livello di protezione da questo tipo di attacco, anche se al prezzo di una minore trasparenza.

Forzare la crittografia dei parametri

Prima di inviare una query con parametri a SQL Server, il provider di dati Microsoft .NET per SQL Server chiede a SQL Server (chiamando sys.sp_describe_parameter_encryption) di analizzare l'istruzione di query e di restituire informazioni sui parametri della query da crittografare. Un'istanza di SQL Server compromessa può fuorviare il provider di dati Microsoft .NET per SQL Server inviando metadati indicanti che il parametro non è destinato a una colonna crittografata, anche se questa è crittografata nel database. Di conseguenza, il provider di dati Microsoft .NET per SQL Server non eseguirà la crittografia del valore del parametro e lo invierà come testo non crittografato all'istanza di SQL Server compromessa.

Per evitare un attacco di questo tipo, un'applicazione può impostare su true la proprietà SqlParameter.ForceColumnEncryption del parametro. Questa impostazione fa sì che il provider di dati Microsoft .NET per SQL Server generi un'eccezione, se i metadati ricevuti dal server indicano che il parametro non deve essere crittografato.

Anche se l'uso della proprietà SqlParameter.ForceColumnEncryption consente di migliorare la sicurezza, ha l'effetto di ridurre la trasparenza della crittografia per l'applicazione client. Se si aggiorna lo schema del database per modificare il set di colonne crittografate, può essere necessario apportare modifiche anche all'applicazione.

L'esempio di codice seguente illustra l'uso della proprietà SqlParameter.ForceColumnEncryption per impedire che i numeri SSN vengano inviati in testo non crittografato al database.

using (SqlCommand cmd = _sqlconn.CreateCommand())
{
    // Use parameterized queries to access Always Encrypted data.

    cmd.CommandText = @"SELECT [SSN], [FirstName], [LastName], [BirthDate] FROM [dbo].[Patients] WHERE [SSN] = @SSN;";

    SqlParameter paramSSN = cmd.CreateParameter();
    paramSSN.ParameterName = @"@SSN";
    paramSSN.DbType = DbType.AnsiStringFixedLength;
    paramSSN.Direction = ParameterDirection.Input;
    paramSSN.Value = ssn;
    paramSSN.Size = 11;
    paramSSN.ForceColumnEncryption = true;
    cmd.Parameters.Add(paramSSN);

    using (SqlDataReader reader = cmd.ExecuteReader())
    {
        // Do something.
    }
}

Configurazione di percorsi attendibili per le chiavi master delle colonne

I metadati di crittografia restituiti da SQL Server per i parametri di query destinati alle colonne crittografate e per i risultati recuperati dalle colonne di crittografia includono il percorso della chiave master della colonna che identifica l'archivio delle chiavi e la posizione della chiave nell'archivio. Se l'istanza di SQL Server è compromessa, può inviare il percorso della chiave che indirizza il provider di dati Microsoft .NET per SQL Server verso la posizione controllata da un utente malintenzionato. Questo processo può causare una perdita di credenziali dell'archivio chiavi, se l'archivio chiavi richiede all'applicazione di eseguire l'autenticazione.

Per impedire attacchi di questo tipo, l'applicazione può specificare l'elenco dei percorsi di chiave attendibili per un determinato server usando la proprietà SqlConnection.ColumnEncryptionTrustedMasterKeyPaths. Se il provider di dati Microsoft .NET per SQL Server riceve un percorso di chiave non incluso nell'elenco dei percorsi di chiave attendibili, verrà generata un'eccezione.

Benché l'impostazione di percorsi di chiave attendibili migliori la sicurezza dell'applicazione, è necessario modificare il codice e/o la configurazione dell'applicazione ogni volta che si ruota la chiave master di colonna, ovvero ogni volta che il percorso di chiave master di colonna cambia.

L'esempio seguente mostra come configurare i percorsi attendibili delle chiavi master delle colonne:

// Configure trusted key paths to protect against fake key paths sent by a compromised SQL Server instance
// First, create a list of trusted key paths for your server
List<string> trustedKeyPathList = new List<string>();
trustedKeyPathList.Add("CurrentUser/my/425CFBB9DDDD081BB0061534CE6AB06CB5283F5Ea");

// Register the trusted key path list for your server
SqlConnection.ColumnEncryptionTrustedMasterKeyPaths.Add(serverName, trustedKeyPathList);

Copia dei dati crittografati usando SqlBulkCopy

Con SqlBulkCopy, è possibile copiare dati, che sono già crittografati e archiviati in una tabella, in un'altra tabella, senza la decrittografia dei dati. A tale scopo, eseguire queste operazioni:

  • Assicurarsi che la configurazione di crittografia della tabella di destinazione sia identica alla configurazione della tabella di origine. In particolare, entrambe le tabelle devono avere le stesse colonne crittografate e le colonne devono essere crittografate usando gli stessi tipi di crittografia e le stesse chiavi di crittografia. Se una o più colonne di destinazione sono crittografate in modo diverso dalla colonna di origine corrispondente, non sarà possibile decrittografare i dati nella tabella di destinazione dopo l'operazione di copia. I dati risulteranno danneggiati.
  • Configurare le due connessioni di database alla tabella di origine e la tabella di destinazione, senza abilitare Always Encrypted.
  • Impostare l'opzione AllowEncryptedValueModifications (vedere SqlBulkCopyOptions).

Nota

Prestare attenzione quando si specifica AllowEncryptedValueModifications. Questa impostazione può causare la corruzione del database, in quanto il provider di dati Microsoft .NET per SQL Server non verifica se i dati siano effettivamente crittografati o se vengano crittografati correttamente usando lo stesso tipo di crittografia, algoritmo e chiave della colonna di destinazione.

Ecco un esempio che copia i dati da una tabella a un'altra. Si presuppone che le colonne SSN e BirthDate siano crittografate.

static public void CopyTablesUsingBulk(string sourceTable, string targetTable)
{
    string sourceConnectionString = "Data Source=server63; Initial Catalog=Clinic; Integrated Security=true";
    string targetConnectionString = "Data Source=server64; Initial Catalog=Clinic; Integrated Security=true";
    using (SqlConnection connSource = new SqlConnection(sourceConnectionString))
    {
        connSource.Open();
        using (SqlCommand cmd = new SqlCommand(string.Format("SELECT [PatientID], [SSN], [FirstName], [LastName], [BirthDate] FROM {0}", sourceTable), connSource))
        {
            using (SqlDataReader reader = cmd.ExecuteReader())
            using (SqlBulkCopy copy = new SqlBulkCopy(targetConnectionString, SqlBulkCopyOptions.KeepIdentity | SqlBulkCopyOptions.AllowEncryptedValueModifications))
            {
                copy.EnableStreaming = true;
                copy.DestinationTableName = targetTable;
                copy.WriteToServer(reader);
            }
        }
    }
}

Riferimento API di Always Encrypted

Spazio dei nomi:Microsoft.Data.SqlClient

Assembly: Microsoft.Data.SqlClient.dll

Nome Descrizione
Classe SqlColumnEncryptionCertificateStoreProvider Un provider dell'archivio chiavi per l'archivio certificati di Windows.
Classe SqlColumnEncryptionCngProvider Un provider di archivi di chiavi per l'API di crittografia di Microsoft: Next Generation (CNG).
Classe SqlColumnEncryptionCspProvider Un provider dell'archivio chiavi per i provider di servizi di crittografia (CSP) basati su Microsoft CAPI.
classe SqlColumnEncryptionKeyStoreProvider Classe base per tutti i provider di archivi di chiavi.
Enumerazione SqlCommandColumnEncryptionSetting Impostazioni per controllare il comportamento della Crittografia sempre attiva per ogni singola query.
Enumerazione SqlConnectionAttestationProtocol Specifica un valore per il protocollo di attestazione quando si usa Always Encrypted con enclave sicure
Enumerazione SqlConnectionColumnEncryptionSetting Impostazioni per abilitare la crittografia e la decrittografia per una connessione al database.
Proprietà SqlConnectionStringBuilder.ColumnEncryptionSetting Ottiene e imposta Always Encrypted nella stringa di connessione.
proprietà SqlConnection.ColumnEncryptionQueryMetadataCacheEnabled Abilita e disabilita la memorizzazione nella cache dei metadati di crittografia delle query.
proprietà SqlConnection.ColumnEncryptionKeyCacheTtl Ottiene e imposta la durata (TTL) per le voci nella cache delle chiavi di crittografia delle colonne.
proprietà SqlConnection.ColumnEncryptionTrustedMasterKeyPaths Consente di impostare un elenco di percorsi principali attendibili per un server di database. Se durante l'elaborazione di una query dell'applicazione il driver riceve un percorso di chiave non incluso nell'elenco, la query non riesce. Questa proprietà fornisce protezione aggiuntiva da attacchi alla sicurezza in cui un'istanza di SQL Server compromessa fornisce falsi percorsi di chiave, causando la perdita delle credenziali dell'archivio chiavi.
Metodo SqlConnection.RegisterColumnEncryptionKeyStoreProviders Consente di registrare provider personalizzati per gli archivi delle chiavi. È un dizionario che esegue il mapping dei nomi dei provider degli archivi delle chiavi per le implementazioni del provider dell'archivio delle chiavi.
SqlCommand Constructor (String, SqlConnection, SqlTransaction, SqlCommandColumnEncryptionSetting) Consente di controllare il comportamento della Crittografia sempre attiva per ogni singola query.
proprietà SqlParameter.ForceColumnEncryption Applica la crittografia di un parametro. Se SQL Server indica al driver che il parametro non deve essere crittografato, la query che usa il parametro non riuscirà. Questa proprietà fornisce protezione aggiuntiva contro attacchi alla sicurezza in cui un'istanza di SQL Server compromessa fornisce al client metadati di crittografia non corretti, causando la divulgazione dei dati.
Parola chiave connection string: Column Encryption Setting=enabled Abilita o disabilita la funzionalità Crittografia sempre attiva per la connessione.

Vedi anche