Utilisation d’Always Encrypted avec le Fournisseur de données Microsoft .NET pour SQL Server

S'applique à : .NET Framework .NET .NET Standard

Cet article fournit des informations sur le développement d’applications .NET à l’aide d’Always Encrypted ou Always Encrypted avec enclaves sécurisées et du Fournisseur de données Microsoft .NET pour SQL Server.

Always Encrypted permet aux applications clientes de chiffrer des données sensibles et de ne jamais révéler les données ou les clés de chiffrement à SQL Server ou Azure SQL Database. Pour garantir cette sécurité, un pilote activé avec Always Encrypted, tel que le Fournisseur de données .NET pour SQL Server, chiffre et déchiffre de manière transparente les données sensibles dans l’application cliente. Le pilote détermine automatiquement les paramètres de requêtes qui correspondent aux colonnes de base de données sensibles (protégées avec Always Encrypted) et chiffre les valeurs de ces paramètres avant de passer les données au serveur. De même, il déchiffre de manière transparente les données récupérées dans les colonnes de base de données chiffrées, qui figurent dans les résultats de la requête. Pour plus d’informations, consultez Développer des applications à l’aide d’Always Encrypted et Développer des applications à l’aide d’Always Encrypted avec enclaves sécurisées.

Prérequis

  • Configurez Always Encrypted dans votre base de données. Ce processus consiste à provisionner des clés Always Encrypted et à configurer le chiffrement pour les colonnes de base de données sélectionnées. Si vous n’avez pas encore de base de données configurée avec Always Encrypted, suivez les instructions du Tutoriel : bien démarrer avec Always Encrypted.
  • Si vous utilisez Always Encrypted avec enclaves sécurisées, consultez Développer des applications en utilisant Always Encrypted avec enclaves sécurisées pour connaître les autres prérequis.
  • Vérifiez que la plateforme .NET requise est installée sur votre ordinateur de développement. Avec Microsoft. Data. SqlClient, la fonctionnalité Always Encrypted est prise en charge pour .NET Framework et .NET Core. Assurez-vous que .NET Framework 4.6 ou version ultérieure ou .NET Core 2.1 ou version ultérieure est configurée comme version cible de la plateforme .NET dans votre environnement de développement. Dans Microsoft.Data.SqlClient versions 2.1.0 et ultérieures, la fonctionnalité Always Encrypted est également prise en charge pour .NET Standard 2.0. Pour utiliser Always Encrypted avec enclaves sécurisés, .NET Standard 2.1 est requis. Si vous voulez utiliser des enclaves VBS sans attestation, Microsoft.Data.SqlClient version 4.1 ou ultérieure est nécessaire. Si vous utilisez Visual Studio, veuillez vous reporter à la Vue d’ensemble ciblant l’infrastructure.

La table suivante récapitule les plateformes .NET requises pour utiliser Always Encrypted avec Microsoft.Data.SqlClient.

Support Always Encrypted Support Always Encrypted avec enclave sécurisée Framework cible Version Microsoft.Data.SqlClient Système d’exploitation
Oui Oui .NET Framework 4.6+ 1.1.0+ Windows
Oui Oui .NET Core 2.1+ 2.1.0+1 Windows, Linux, macOS
Oui Non .NET Standard 2.0 2.1.0+ Windows, Linux, macOS
Oui Oui .NET Standard 2.1+ 2.1.0+ Windows, Linux, macOS

Notes

1 Avant Microsoft.Data.SqlClient version 2.1.0, Always Encrypted est pris en charge uniquement sur Windows.

Activation d’Always Encrypted pour les requêtes d’application

Le moyen le plus simple d’activer le chiffrement des paramètres et le déchiffrement des résultats de requête ciblant des colonnes chiffrées est de définir la valeur du mot clé de la chaîne de connexion Column Encryption Setting sur activé.

Voici un exemple de chaîne de connexion activant Always Encrypted :

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

L’exemple d’extrait de code suivant est équivalent et utilise la propriété 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 peut également être activé pour les requêtes individuelles. Consultez la section Contrôle de l’impact d’Always Encrypted sur les performances ci-dessous. L’activation d’Always Encrypted ne suffit pas à la réussite du chiffrement ou du déchiffrement. Vous devez également vérifier ce qui suit :

  • L’application dispose des autorisations de base de données VIEW ANY COLUMN MASTER KEY DEFINITION et VIEW ANY COLUMN ENCRYPTION KEY DEFINITION qui sont nécessaires pour accéder aux métadonnées des clés Always Encrypted dans la base de données. Pour plus d’informations, consultez la section Autorisations de bases de données dans Always Encrypted (moteur de base de données).
  • L’application peut accéder à la clé principale de colonne qui protège les clés de chiffrement de colonne, qui chiffrent les colonnes de base de données interrogées.

Activation d’Always Encrypted avec des enclaves sécurisées

À partir de la version 1.1.0 de Microsoft.Data.SqlClient, le pilote prend en charge Always Encrypted avec enclaves sécurisées.

Pour obtenir des informations d’ordre général, consultez Développer des applications à l’aide d’Always Encrypted avec enclaves sécurisées.

Pour activer les calculs d’enclave pour une connexion de base de données, vous devez définir les mots clés de chaînes de connexion suivants, en plus de l’activation d’Always Encrypted (comme expliqué dans la section précédente) :

  • Attestation Protocol : spécifie un protocole d’attestation.

    • Si ce mot clé n’est pas spécifié, les enclaves sécurisées sont désactivées sur la connexion.
    • Si vous utilisez SQL Server avec des enclaves VBS (Sécurité basée sur la virtualisation) et SGH (Service Guardian hôte), la valeur de ce mot clé doit être HGS.
    • Si vous utilisez Azure SQL Database avec des enclaves Intel SGX et Microsoft Azure Attestation, la valeur de ce mot clé doit être AAS.
    • Si vous utilisez Azure SQL Database ou SQL Server avec des enclaves VBS et que vous voulez renoncer à l’attestation, la valeur de ce mot clé doit être None. Nécessite la version 4.1 ou ultérieure.

    Notes

    « None » (pas d’attestation) est la seule option actuellement prise en charge pour les enclaves VBS dans Azure SQL Database.

  • Enclave Attestation URL : spécifie une URL d’attestation (un point de terminaison de service d’attestation). Vous devez obtenir une URL d’attestation pour votre environnement auprès de votre administrateur de services fédérés d’attestation.

Pour obtenir un tutoriel pas à pas, consultez Tutoriel : Développer une application .NET en utilisant Always Encrypted avec enclaves sécurisées.

Récupération et modification des données dans des colonnes chiffrées

Lorsque vous activez Always Encrypted pour les requêtes d’application, vous pouvez utiliser les API SqlClient standard (consultez Extraction et modification de données dans ADO.NET) ou les API du Fournisseur de données Microsoft .NET pour SQL Server qui sont définies dans Microsoft.Data.SqlClient Namespace, pour récupérer ou modifier les données de colonnes de base de données chiffrées. Si votre application a les autorisations de base de données nécessaires et qu’elle a accès à la clé principale de colonne, le Fournisseur de données Microsoft .NET pour SQL Server chiffre tous les paramètres de requête qui ciblent des colonnes chiffrées et déchiffre les données extraites des colonnes chiffrées, en retournant les valeurs de texte en clair des types .NET qui correspondent aux types de données SQL Server définis pour les colonnes du schéma de base de données. Si Always Encrypted n’est pas activé, les requêtes ayant des paramètres qui ciblent des colonnes chiffrées échouent. Une requête peut toujours récupérer des données à partir de colonnes chiffrées, tant qu’aucun de ses paramètres ne cible des colonnes chiffrées. Toutefois, le Fournisseur de données Microsoft .NET pour SQL Server ne tente pas de déchiffrer les valeurs extraites des colonnes chiffrées et l’application reçoit des données chiffrées binaires (sous la forme de tableaux d’octets).

Le tableau ci-dessous récapitule le comportement des requêtes, selon qu’Always Encrypted est activé ou non :

Caractéristique de la requête Always Encrypted est activé et l’application peut accéder aux clés et à leurs métadonnées Always Encrypted est activé et l’application ne peut pas accéder aux clés et à leurs métadonnées Always Encrypted est désactivé
Requêtes avec des paramètres ciblant des colonnes chiffrées. Des valeurs de paramètres sont chiffrées en toute transparence. Error Error
Requêtes qui récupèrent des données à partir de colonnes chiffrées, sans paramètres ciblant des colonnes chiffrées. Les résultats de colonnes chiffrées sont déchiffrés de manière transparente. L’application reçoit des valeurs en texte clair des types de données .NET correspondant aux types SQL Server configurés pour les colonnes chiffrées. Error Les résultats des colonnes chiffrées ne sont pas déchiffrés. L’application reçoit des valeurs chiffrées sous la forme de tableaux d’octets (byte[]).

Les exemples suivants illustrent la récupération et la modification de données dans des colonnes chiffrées. Ces exemples impliquent une table cible avec le schéma ci-dessous. Les colonnes SSN et BirthDate sont chiffrées.

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

Exemple d’insertion de données

Cet exemple insère une ligne dans la table Patients. Notez les points suivants :

  • L’exemple de code ne contient aucun élément propre au chiffrement. Le Fournisseur de données Microsoft .NET pour SQL Server détecte et chiffre automatiquement les paramètres paramSSN et paramBirthdate qui ciblent des colonnes chiffrées. Ce comportement rend le chiffrement transparent pour l’application.
  • Les valeurs insérées dans les colonnes de base de données, y compris les colonnes chiffrées, sont passées en tant qu’objets SqlParameter . L’utilisation de SqlParameter est facultative pour l’envoi de valeurs aux colonnes non chiffrées (même si elle est vivement recommandée, car elle contribue à empêcher l’injection SQL), mais elle est nécessaire pour les valeurs qui ciblent des colonnes chiffrées. Si les valeurs insérées dans les colonnes SSN ou BirthDate ont été passées en tant que littéraux incorporés dans l’instruction de requête, la requête échouera car le fournisseur de données Microsoft .NET pour SQL Server ne pourra pas déterminer les valeurs des colonnes chiffrées cibles et ne chiffrera donc pas les valeurs. Par conséquent, le serveur les rejettera en les considérant comme incompatibles avec les colonnes chiffrées.
  • Le type de données du paramètre ciblant la colonne SSN est défini sur une chaîne ANSI (non Unicode) qui est mappée vers le type de données SQL Server char/varchar. Si le type du paramètre est défini sur une chaîne Unicode (String) mappée à nchar/nvarchar, la requête échoue, car Always Encrypted ne prend pas en charge les conversions de valeurs nchar/nvarchar chiffrées en valeurs char/varchar chiffrées. Pour plus d’informations sur les mappages de type de données, consultez Mappages de type de données SQL Server .
  • Le type de données du paramètre inséré dans la colonne BirthDate est explicitement défini sur le type de données SQL Server cible à l’aide de la Propriété SqlParameter.SqlDbType, au lieu d’utiliser le mappage implicite des types .NET vers les types de données SQL Server appliqués lors de l’utilisation de la Propriété SqlParameter.DbType. Par défaut, la structure DateHeure est mappée vers le type de données SQL Server DateHeure. Étant donné que les données de la colonne BirthDate sont de type date et qu’Always Encrypted ne prend pas en charge la conversion de valeurs datetime chiffrées en valeurs date chiffrées, l’utilisation du mappage par défaut entraînerait une erreur.
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();
}

Exemple de récupération de données en texte clair

L’exemple suivant montre le filtrage de données basé sur des valeurs chiffrées, ainsi que la récupération de données en texte clair à partir de colonnes chiffrées.

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

Notes

  • La valeur utilisée dans la clause WHERE pour filtrer la colonne SSN doit être transmise à l’aide de SqlParameter, afin que le Fournisseur de données .NET Microsoft pour SQL Server puisse la chiffrer de manière transparente avant de l’envoyer à la base de données.

  • Toutes les valeurs imprimées par le programme sont sous la forme de texte en clair, car le Fournisseur de données .NET Microsoft pour SQL Server déchiffre de manière transparente les données récupérées à partir des colonnes SSN et BirthDate.

  • Les requêtes peuvent effectuer des comparaisons d’égalité sur des colonnes si elles sont chiffrées à l’aide du chiffrement déterministe.

Exemple de récupération de données chiffrées

Si Always Encrypted n’est pas activé, une requête peut toujours récupérer des données à partir de colonnes chiffrées, tant qu’aucun de ses paramètres ne ciblent des colonnes chiffrées.

L’exemple suivant montre comment récupérer des données chiffrées binaires à partir de colonnes chiffrées.

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

Notes

  • Étant donné qu’Always Encrypted n’est pas toujours activé dans la chaîne de connexion, la requête retourne des valeurs SSN et BirthDatechiffrées sous la forme de tableaux d’octets (le programme convertit les valeurs en chaînes).

  • Une requête qui récupère des données à partir de colonnes chiffrées lorsqu’Always Encrypted est désactivé peut avoir des paramètres, tant qu’aucun d’eux ne cible une colonne chiffrée. La requête ci-dessus filtre par nom (LastName), qui n’est pas chiffré dans la base de données. Si la requête filtre par SSN ou BirthDate, la requête échoue.

Éviter les problèmes courants lors de l’interrogation de colonnes chiffrées

Cette section décrit des catégories d’erreurs courantes liées à l’interrogation des colonnes chiffrées à partir d’applications .NET et fournit des conseils sur la façon de les éviter.

Erreurs liées à la conversion de types de données non pris en charge

Always Encrypted ne prend en charge que peu de conversions de types de données chiffrées. Consultez Always Encrypted pour obtenir la liste détaillée des conversions de types prises en charge. Procédez comme suit pour éviter les erreurs de conversion de type de données :

  • Définissez les types de paramètres ciblant les colonnes chiffrées pour que le type de données SQL Server du paramètre soit exactement le même que le type de la colonne cible ou pour que soit prise en charge la conversion du type de données SQL Server du paramètre vers le type cible de la colonne. Vous pouvez appliquer le mappage des types de données .NET vers des types de données SQL Server spécifiques à l’aide de la propriété SqlParameter.SqlDbType.
  • Vérifiez que la précision et l’échelle des paramètres ciblant les colonnes des types de données SQL Server decimal et numeric sont les mêmes que celles configurées pour la colonne cible.
  • Vérifiez que la précision des paramètres ciblant des colonnes des types de données SQL Server datetime2, datetimeoffset ou time n’est pas supérieure à celle de la colonne cible dans les requêtes qui modifient les valeurs de la colonne cible.

Erreurs dues au passage de texte en clair au lieu de valeurs chiffrées

Les valeurs qui ciblent une colonne chiffrée doivent être chiffrées dans l’application. Toute tentative d’insertion, de modification ou de filtrage par une valeur de texte en clair dans une colonne chiffrée entraîne une erreur similaire à celle-ci :

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'

Pour éviter ces erreurs, procédez comme suit :

  • Activez Always Encrypted pour les requêtes d’application ciblant des colonnes chiffrées (pour la chaîne de connexion ou dans l’objet SqlCommand pour une requête spécifique).
  • Utilisez SqlParameter pour envoyer des données ciblant des colonnes chiffrées. L’exemple suivant illustre une requête qui filtre incorrectement une colonne chiffrée (SSN) à l’aide d’un littéral (ou d’une constante), au lieu de transférer le littéral à l’intérieur d’un objet SqlParameter.
using (SqlCommand cmd = connection.CreateCommand())
{
    cmd.CommandText = @"SELECT [SSN], [FirstName], [LastName], [BirthDate] FROM [dbo].[Patients] WHERE SSN = '795-73-9838'";
    cmd.ExecuteNonQuery();
}

Utilisation de magasins de clés principales de colonne

Pour chiffrer une valeur de paramètre ou déchiffrer les données des résultats de requête, le Fournisseur de données Microsoft .NET pour SQL Server doit obtenir une clé de chiffrement de colonne qui soit configurée pour la colonne cible. Les clés de chiffrement de colonne sont stockées sous forme chiffrée dans les métadonnées de la base de données. Chaque clé de chiffrement de colonne a une clé principale de colonne correspondante qui a été utilisée pour chiffrer la clé de chiffrement de colonne. Les métadonnées de la base de données ne stockent pas les clés principales de colonne. Elles contiennent uniquement des informations sur un magasin de clés contenant une clé principale de colonne et sur l’emplacement de cette clé dans le magasin.

Pour obtenir une valeur en texte clair d’une clé de chiffrement de colonne, le Fournisseur de données Microsoft .NET pour SQL Server obtient d’abord les métadonnées relatives à la clé de chiffrement de colonne et à la clé principale de colonne correspondante. Il utilise ensuite les informations trouvées dans les métadonnées pour contacter le magasin de clés qui contient la clé principale de colonne et pour chiffrer la clé de chiffrement de colonne chiffrée. Le Fournisseur de données Microsoft .NET pour SQL Server communique avec un magasin de clés à l’aide d’un fournisseur de magasin de clé principales de colonne, qui est une instance de classe dérivée de la classe SqlColumnEncryptionKeyStoreProvider.

Le processus pour obtenir une clé de chiffrement de colonne est le suivant :

  1. Si Always Encrypted est activé pour une requête, le Fournisseur de données Microsoft .NET pour SQL Server appelle en toute transparence sys.sp_describe_parameter_encryption pour récupérer les métadonnées de chiffrement pour les paramètres ciblant les colonnes chiffrées, si la requête possède des paramètres. Pour les données chiffrées contenues dans les résultats d’une requête, SQL Server affecte automatiquement des métadonnées de chiffrement. Les informations sur la clé principale de colonne sont les suivantes :

    • Le nom d’un fournisseur de magasins de clés qui encapsule un magasin de clés contenant la clé principale de colonne.
    • Le chemin d’accès à une clé qui spécifie l’emplacement de la clé CMK dans le magasin de clés.

    Les informations sur la clé de chiffrement de colonne sont les suivantes :

    • La valeur chiffrée d’une clé de chiffrement de colonne.
    • Le nom de l’algorithme utilisé pour chiffrer la clé CEK.
  2. Le Fournisseur de données Microsoft .NET pour SQL Server utilise le nom du fournisseur du magasin de clés principales de colonne pour rechercher l’objet du fournisseur, qui est une instance de classe dérivée de la classe SqlColumnEncryptionKeyStoreProvider, dans une structure de données interne.

  3. Pour déchiffrer la clé de chiffrement de colonne, le Fournisseur de données Microsoft .NET pour SQL Server appelle la méthode SqlColumnEncryptionKeyStoreProvider.DecryptColumnEncryptionKey(), en transférant le chemin d’accès de la clé principale de colonne, la valeur chiffrée de la clé de chiffrement de colonne et le nom de l’algorithme de chiffrement utilisé pour générer la clé de chiffrement de la colonne chiffrée.

Utilisation des fournisseurs de magasin de clés principales de colonne intégrés

Le Fournisseur de données Microsoft .NET pour SQL Server est fourni avec les fournisseurs de magasin de clés principales de colonne intégrés suivants, qui sont pré-inscrits avec des noms de fournisseurs spécifiques (utilisés pour leur recherche). Ces fournisseurs de magasins de clés intégrés ne sont pris en charge que sur Windows.

Classe Description Nom de fournisseur (pour la recherche) Plateforme
Classe SqlColumnEncryptionCertificateStoreProvider Fournisseur du magasin de certificats Windows. MSSQL_CERTIFICATE_STORE Windows
Classe SqlColumnEncryptionCngProvider Fournisseur de magasin de clés pour l’ API de chiffrement Microsoft : API de la prochaine génération (CNG). En général, ce type de magasin est un module de sécurité matériel, c’est-à-dire un périphérique physique qui protège et gère les clés numériques et fournit un traitement du chiffrement. MSSQL_CNG_STORE Windows
Classe SqlColumnEncryptionCspProvider Fournisseur de magasin de clés pour l’ API de chiffrement Microsoft (CAPI). En général, ce type de magasin est un module de sécurité matériel, c’est-à-dire un périphérique physique qui protège et gère les clés numériques et fournit un traitement du chiffrement. MSSQL_CSP_PROVIDER Windows

Vous n’avez pas besoin d’apporter des modifications au code de l’application pour utiliser ces fournisseurs, mais notez les points suivants :

  • Vous (ou votre administrateur de base de données) devez vérifier que le nom du fournisseur (configuré dans les métadonnées de clé principale de colonne) est correct et que le chemin de la clé principale de colonne est valide pour un fournisseur donné. Nous vous recommandons de configurer les clés à l’aide d’outils tels que SQL Server Management Studio. Cet outil génère automatiquement des noms de fournisseurs et des chemins de clés valides lorsque l’instruction CREATE COLUMN MASTER KEY (Transact-SQL) est émise. Pour plus d’informations, consultez Configurer Always Encrypted à l’aide de SQL Server Management Studio et Configurer Always Encrypted à l’aide de PowerShell.
  • Vérifiez que votre application peut accéder à la clé dans le magasin de clés. Pour ce processus, vous devrez peut-être accorder à votre application l’accès à la clé et/ou au magasin de clés (selon le magasin de clés) ou effectuer d’autres étapes de configuration spécifiques au magasin de clés. Par exemple, pour accéder à un magasin de clés qui implémente CNG ou CAPI (comme un module de sécurité matériel), vous devrez vérifier qu’une bibliothèque implémentant CNG ou CAPI pour votre magasin est installée sur la machine de votre application. Pour plus d’informations, consultez Créer et stocker des clés principales de colonne pour Always Encrypted.

Utilisation du fournisseur Azure Key Vault

Azure Key Vault est un outil est très pratique qui permet de stocker et de gérer des clés principales de colonne Always Encrypted, en particulier si vos applications sont hébergées dans Azure. Le fournisseur de données Microsoft .NET pour SQL Server ne comprend pas de fournisseur de magasin de clés principales de colonne intégré pour Azure Key Vault. Toutefois, celui-ci est disponible sous la forme d’un package NuGet (Microsoft.Data.SqLClient.AlwaysEncrypted.AzureKeyVaultProvider) que vous pouvez facilement intégrer à votre application. Pour plus d’informations, consultez Always Encrypted - Protéger les données sensibles de la base de données SQL à l’aide du chiffrement de base de données et stocker vos clés de chiffrement dans Azure Key Vault.

Classe Description Nom de fournisseur (pour la recherche) Plateforme
Classe SqlColumnEncryptionAzureKeyVaultProvider Fournisseur pour Azure Key Vault. AZURE_KEY_VAULT Windows, Linux, macOS

Capacité de prise en charge de .NET

Version Version Microsoft.Data.SqlClient Plateformes .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+

À partir de v3.0.0, Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider prend en charge les fonctionnalités de mise en cache de clés de chiffrement de colonne lors de l’inscription du fournisseur à l’aide des API SqlConnection.RegisterColumnEncryptionKeyStoreProvidersOnConnection ou SqlCommand.RegisterColumnEncryptionKeyStoreProvidersOnCommand.

À partir de la version 2.0.0, Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider prend en charge les nouvelles API Azure.Core et Azure.Identity pour effectuer l’authentification auprès d’Azure Key Vault. Une instance de l’implémentation TokenCredential peut maintenant être passée aux constructeurs SqlColumnEncryptionAzureKeyVaultProvider pour initialiser l’objet fournisseur Azure Key Vault.

Notes

Microsoft.Data.SqLClient.AlwaysEncrypted.AzureKeyVaultProvider prend en charge les coffres et les HSM managés dans Azure Key Vault.

Pour obtenir des exemples illustrant le chiffrement/déchiffrement avec Azure Key Vault, consultez Utilisation d’Azure Key Vault avec Always Encrypted et Utilisation d’Azure Key Vault avec Always Encrypted avec enclaves sécurisées.

Implémentation d’un fournisseur de magasin de clés principales de colonne personnalisé

Si vous voulez stocker des clés principales de colonne dans un magasin de clés qui n’est pas pris en charge par un fournisseur existant, vous pouvez implémenter un fournisseur personnalisé en étendant la classe SqlColumnEncryptionKeyStoreProvider et en inscrivant le fournisseur à l’aide de l’une des méthodes suivantes :

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

Priorité du cache de clés de chiffrement de colonne

Cette section s’applique à la version 3.0 et aux versions ultérieures du Fournisseur de données Microsoft .NET pour SQL Server.

Les clés de chiffrement de colonne (CEK) déchiffrées par les fournisseurs de magasins de clés personnalisés inscrits sur une instance de connexion ou de commande ne sont pas mises en cache par le fournisseur de données Microsoft .NET pour SQL Server. Les fournisseurs de magasins de clés personnalisés doivent implémenter leur propre mécanisme de mise en cache de clés CEK.

À partir de la v3.0.0 de Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider, chaque instance de Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider possède sa propre implémentation de la mise en cache CEK. Quand elles sont inscrites sur une instance de connexion ou de commande, les clés CEK déchiffrées par une instance de Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider sont effacées lorsque cette instance sort de l’étendue :

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

Notes

La mise en cache de clés CEK implémentée par les fournisseurs de magasins de clés personnalisés sera désactivée par le pilote si l’instance de fournisseur de magasin de clés est inscrite dans le pilote globalement à l’aide de la méthode SqlConnection.RegisterColumnEncryptionKeyStoreProviders. Toute implémentation de la mise en cache de clés CEK doit référencer la valeur de SqlColumnEncryptionKeyStoreProvider.ColumnEncryptionKeyCacheTtl avant de mettre en cache une clé CEK et ne pas la mettre en cache si la valeur est égale à zéro. Cela permet d’éviter une mise en cache dupliquée et une confusion possible des utilisateurs quand ils tentent de configurer la mise en cache de clés.

Inscription d’un fournisseur de magasin de clés principales de colonne personnalisé

Cette section s’applique aux versions 3.0 et ultérieures du fournisseur.

Les fournisseurs de magasins de clés principales personnalisés peuvent être inscrits auprès du pilote dans trois couches différentes. La priorité des trois inscriptions est la suivante :

  • L’inscription par commande est vérifiée si elle n’est pas vide.
  • Si l’inscription par commande est vide, l’inscription par connexion est vérifiée si elle n’est pas vide.
  • Si l’inscription par connexion est vide, l’inscription globale est vérifiée.

Une fois qu’un fournisseur de magasin de clés est trouvé au niveau d’une inscription, le pilote ne revient PAS aux autres inscriptions pour rechercher un fournisseur. Si des fournisseurs sont inscrits, mais que le fournisseur approprié est introuvable à un niveau, une exception est levée, contenant uniquement les fournisseurs inscrits dans l’inscription qui a été vérifiée.

Les fournisseurs de magasins de clés principales de colonne intégrés qui sont disponibles pour le magasin de certificats Windows, le magasin CNG et CSP sont préinscrits.

Les trois niveaux d’inscription prennent en charge différents scénarios lors de l’interrogation de données chiffrées. La méthode appropriée permet de s’assurer qu’un utilisateur d’une application peut accéder aux données en texte clair s’il parvient à fournir la clé principale de colonne requise, en s’authentifiant auprès du magasin de clés qui la contient.

Les applications qui partagent une instance SqlConnection entre plusieurs utilisateurs peuvent utiliser SqlCommand.RegisterColumnEncryptionKeyStoreProvidersOnCommand. Chaque utilisateur doit inscrire un fournisseur de magasin de clés sur une instance SqlCommand avant d’exécuter une requête pour accéder à une colonne chiffrée. Si le fournisseur de magasin de clés peut accéder à la clé principale de colonne requise dans le magasin de clés à l’aide des informations d’identification fournies par l’utilisateur, la requête réussit.

Les applications qui créent une instance SqlConnection pour chaque utilisateur peuvent utiliser SqlConnection.RegisterColumnEncryptionKeyStoreProvidersOnConnection. Les fournisseurs de magasins de clés inscrits avec cette méthode peuvent être utilisés par la connexion pour toutes les requêtes qui accèdent à des données chiffrées.

Les fournisseurs de magasins de clés inscrits avec SqlConnection.RegisterColumnEncryptionKeyStoreProviders utilisent l’identité fournie par l’application lors de l’authentification auprès du magasin de clés.

L’exemple suivant illustre la priorité des fournisseurs de magasins de clés principales de colonne personnalisés inscrits sur une instance de connexion :

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’exemple suivant illustre la priorité des fournisseurs de magasins de clés principales de colonne personnalisés inscrits sur une instance de commande :

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

Utilisation des fournisseurs de magasin de clés principales de colonne pour la mise en service des clés par programmation

Quand le fournisseur de données Microsoft .NET pour SQL Server accède à des colonnes chiffrées, il localise et appelle de manière transparente le fournisseur de magasin de clés principales de colonne qui convient pour déchiffrer les clés de chiffrement de colonne. En règle générale, un code d’application normal n’appelle pas directement les fournisseurs de magasin de clés principales de colonne. Vous pouvez toutefois instancier et appeler explicitement un fournisseur afin de créer et de gérer par programmation les clés Always Encrypted, dans le but de générer une clé de chiffrement de colonne chiffrée et de déchiffrer une clé de chiffrement de colonne (par exemple, dans le cadre d’une rotation des clés principales de colonne). Pour plus d’informations, consultez Vue d’ensemble de la gestion des clés pour Always Encrypted. L’implémentation de vos propres outils de gestion de clés peut être nécessaire uniquement si vous utilisez un fournisseur de magasin de clés personnalisé. Quand vous utilisez des clés stockées dans des magasins de clés (pour lesquels des fournisseurs intégrés existent) ou dans Azure Key Vault, vous pouvez utiliser les outils existants, tels que SQL Server Management Studio ou PowerShell, pour gérer et configurer les clés. L’exemple ci-dessous illustre la génération d’une clé de chiffrement de colonne et l’utilisation de la classe SqlColumnEncryptionCertificateStoreProvider pour chiffrer la clé avec un certificat.

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

Contrôle de l’impact d’Always Encrypted sur les performances

Always Encrypted étant une technologie de chiffrement côté client, la surcharge des performances s’observe côté client et non dans la base de données. Outre le coût des opérations de chiffrement et de déchiffrement, les autres sources de surcharge des performances côté client sont les suivantes :

  • Allers-retours supplémentaires avec la base de données afin de récupérer des métadonnées pour les paramètres de requête.
  • Appels au magasin de clés principales de colonne pour accéder à une clé principale de colonne.

Cette section décrit les optimisations des performances intégrées au Fournisseur Microsoft .NET pour SQL Server et comment vous pouvez contrôler l’impact sur les performances des deux facteurs ci-dessus.

Contrôle des allers-retours vers la base de données en vue de la récupération des métadonnées pour les paramètres de requête

Par défaut, si Always Encrypted est activé pour une connexion, le Fournisseur de données Microsoft .NET pour SQL Server appelle sys.sp_describe_parameter_encryption pour chaque requête paramétrable, en passant l’instruction de requête (sans valeurs de paramètre) à SQL Server. sys.sp_describe_parameter_encryption analyse l’instruction de requête pour vérifier si des paramètres doivent être chiffrés, et, si tel est le cas, pour chacun, il renvoie les informations relatives au chiffrement qui autorisent le Fournisseur de données Microsoft .NET pour SQL Server à chiffrer des valeurs de paramètres. Ce comportement garantit un haut niveau de transparence à l’application cliente. L’application (et le développeur d’applications) n’ont pas besoin de connaître les requêtes qui accèdent aux colonnes chiffrées, tant que les valeurs ciblant les colonnes chiffrées sont transmises au Fournisseur de données Microsoft .NET pour SQL Server dans des objets SqlParameter.

Mise en cache des métadonnées de requête

Le Fournisseur de données Microsoft .NET pour SQL Server met en cache les résultats de sys.sp_describe_parameter_encryption pour chaque instruction de requête. Par conséquent, si la même instruction de requête est exécutée plusieurs fois, le pilote appelle sys.sp_describe_parameter_encryption une seule fois. La mise en cache des métadonnées de chiffrement pour les instructions de requête réduit sensiblement le coût en termes de performances de l’extraction des métadonnées à partir de la base de données. La mise en cache est activée par défaut. Vous pouvez désactiver la mise en cache des métadonnées de paramètre en définissant la propriété SqlConnection.ColumnEncryptionQueryMetadataCacheEnabled sur false, bien que cela ne soit pas recommandé, sauf dans de très rares cas comme celui décrit ci-dessous :

Supposons une base de données ayant deux schémas différents : s1 et s2. Chaque schéma contient une table portant le même nom : t. Les définitions des tables s1.t et s2.t sont identiques, à l’exception des propriétés relatives au chiffrement : une colonne nommée c dans s1.t n’est pas chiffrée, et l’est dans s2.t. La base de données comporte deux utilisateurs : u1 et u2. Le schéma par défaut des utilisateurs u1 est s1. Le schéma par défaut pour u2 est s2. Une application .NET ouvre deux connexions sur la base de données, en empruntant l’identité de l’utilisateur u1 sur une connexion et celle de l’utilisateur u2 sur une autre. L’application envoie une requête avec un paramètre ciblant la colonne c sur la connexion pour l’utilisateur u1 (le schéma de l’utilisateur par défaut est utilisé, dans la mesure où la requête ne spécifie aucun schéma). Ensuite, l’application envoie la même requête sur la connexion pour l’utilisateur u2. Si la mise en cache des métadonnées de la requête est activée, après la première requête, le cache sera rempli avec les métadonnées en indiquant que la colonne c, ciblée par le paramètre de requête, n’est pas chiffrée. Dans la mesure où la deuxième requête comporte la même instruction de requête, les informations stockées dans le cache seront utilisées. Par conséquent, le pilote envoie la requête sans chiffrer le paramètre (qui est incorrect, car la colonne cible, s2.t.c, est chiffrée), entraînant la fuite de la valeur de texte en clair du paramètre vers le serveur. Le serveur détecte cette incompatibilité et force le pilote à actualiser le cache. L’application renvoie alors en toute transparence la requête avec la valeur de paramètre correctement chiffrée. Dans ce cas, la mise en cache doit être désactivée pour empêcher toute fuite de valeurs sensibles vers le serveur.

Configuration d’Always Encrypted au niveau de la requête

Pour contrôler l’impact sur les performances de la récupération des métadonnées de chiffrement pour les requêtes paramétrables, vous pouvez activer Always Encrypted pour chaque requête, au lieu de le configurer pour la connexion. De cette façon, sys.sp_describe_parameter_encryption est appelé uniquement pour les requêtes dont les paramètres ciblent des colonnes chiffrées. Notez, toutefois, que de cette façon, vous réduisez la transparence du chiffrement. Si vous modifiez les propriétés de chiffrement de vos colonnes de base de données, vous devrez modifier le code de votre application pour l’aligner sur les modifications du schéma.

Notes

La définition de Always Encrypted au niveau de la requête limite le gain de performances de la mise en cache des métadonnées de chiffrement de paramètres.

Pour contrôler le comportement d’Always Encrypted des requêtes, vous devez utiliser ce constructeur de SqlCommand et SqlCommandColumnEncryptionSetting. Voici quelques conseils utiles :

  • Si la plupart des requêtes qu'une application cliente exécute ont accès aux colonnes chiffrées :
    • Définissez le mot clé de la chaîne de connexion Paramètre de chiffrement de colonne sur Activé.
    • Définissez SqlCommandColumnEncryptionSetting sur Désactivé pour les requêtes qui n’ont pas accès aux colonnes chiffrées. Ce paramètre désactive à la fois l’appel de sys.sp_describe_parameter_encryption et la tentative de déchiffrement des valeurs du jeu de résultats.
    • Définissez SqlCommandColumnEncryptionSetting sur ResultSetOnly pour les requêtes individuelles qui n’ont aucun paramètre exigeant un chiffrement, mais qui récupèrent des données de colonnes chiffrées. Ce paramètre désactive l’appel de sys.sp_describe_parameter_encryption et le chiffrement des paramètres. La requête est alors en mesure de déchiffrer les résultats des colonnes de chiffrement.
  • Si la plupart des requêtes qu'une application cliente exécute n'ont pas accès aux colonnes chiffrées :
    • Définissez le mot clé de la chaîne de connexion Paramètre de chiffrement de colonne sur Désactivé.
    • Définissez SqlCommandColumnEncryptionSetting sur Activé pour les requêtes qui ont des paramètres exigeant un chiffrement. Ce paramètre active à la fois l’appel de sys.sp_describe_parameter_encryption et le déchiffrement des résultats de requête récupérés à partir des colonnes chiffrées.
    • Définissez SqlCommandColumnEncryptionSetting sur ResultSetOnly pour les requêtes qui n’ont aucun paramètre exigeant un chiffrement, mais qui récupèrent des données de colonnes chiffrées. Ce paramètre désactive l’appel de sys.sp_describe_parameter_encryption et le chiffrement des paramètres. La requête est alors en mesure de déchiffrer les résultats des colonnes de chiffrement.

Dans l’exemple ci-dessous, Always Encrypted est désactivé pour la connexion de base de données. La requête envoyée par l’application comprend un paramètre qui cible la colonne LastName qui n’est pas chiffrée. La requête récupère les données des colonnes SSN et BirthDate qui sont toutes deux chiffrées. Dans ce cas, il n’est pas nécessaire d’appeler sys.sp_describe_parameter_encryption pour récupérer les métadonnées de chiffrement. Toutefois, le déchiffrement des résultats de requête doit être activé, afin que l’application puisse recevoir des valeurs en texte clair à partir des deux colonnes chiffrées. Le paramètre SqlCommandColumnEncryptionSettingResultSetOnly est utilisé à cette fin.

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

Mise en cache des clés de chiffrement de colonne

Pour réduire le nombre d’appels vers un magasin de clés principales de colonne afin de déchiffrer les clés de chiffrement de colonne, le Fournisseur de données Microsoft .NET pour SQL Server met en cache les clés de chiffrement de colonne en texte en clair dans la mémoire. Une fois que le fournisseur a reçu la valeur de clé de chiffrement de colonne chiffrée à partir des métadonnées de la base de données, le pilote tente d’abord de trouver la clé de chiffrement de colonne en texte en clair correspondant à la valeur de clé chiffrée. Le pilote appelle le magasin de clés qui contient la clé principale de colonne uniquement s’il ne peut pas trouver la valeur de la clé de chiffrement de colonne chiffrée dans le cache.

Les entrées du cache sont supprimées après un intervalle de durée de vie configurable pour des raisons de sécurité. La valeur de durée de vie par défaut est de 2 heures. Si vous avez des exigences de sécurité plus strictes concernant la durée pendant laquelle les clés de chiffrement de colonne peuvent être mises en cache en texte clair dans l’application, vous pouvez la modifier à l’aide de la propriété SqlConnection.ColumnEncryptionKeyCacheTtl.

Les fournisseurs de magasins de clés personnalisés inscrits à l’aide de SqlConnection.RegisterColumnEncryptionKeyStoreProvidersOnConnection et SqlCommand.RegisterColumnEncryptionKeyStoreProvidersOnCommand n’auront pas leurs clés de chiffrement de colonne déchiffrée mises en cache par le Fournisseur de données Microsoft .NET pour SQL Server. Au lieu de cela, les fournisseurs de magasins de clés personnalisés doivent implémenter leur propre mécanisme de mise en cache de clés. Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProviderv3.0.0 et les versions supérieures disposent de leur propre mise en cache.

Pour prendre en charge les scénarios dans lesquels différents utilisateurs d’une même application peuvent exécuter plusieurs requêtes, des fournisseurs de stockage de clés personnalisés peuvent être associés à un utilisateur et enregistrés sur une connexion ou une instance de commande spécifique à cet utilisateur. L’exemple suivant montre comment une instance de Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider peut être réutilisée sur différents objets SqlCommand pour le même utilisateur. Son cache de clé de chiffrement de colonne est conservé sur plusieurs requêtes, ce qui réduit le nombre d’allers-retours vers le magasin de clés :

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

Activation d’une protection supplémentaire pour un serveur SQL Server compromis

Par défaut, le Fournisseur de données Microsoft .NET pour SQL Server repose sur le système de base de données (SQL Server ou Azure SQL Database) pour fournir des métadonnées indiquant les colonnes de la base de données à chiffrer et de quelle manière. Les métadonnées de chiffrement permettent au Fournisseur de données Microsoft .NET pour SQL Server de chiffrer des paramètres de requête et de déchiffrer des résultats de requête sans aucune entrée de l’application, ce qui réduit considérablement le nombre de modifications requises dans l’application. Toutefois, si le processus SQL Server est compromis et qu’un attaquant falsifie les métadonnées envoyées par SQL Server au Fournisseur de données Microsoft .NET pour SQL Server, ce dernier peut être en mesure de dérober des informations sensibles. Cette section décrit les API qui fournissent un niveau de protection supplémentaire contre ce type d’attaque, mais au prix d’une réduction de la transparence.

Forcer le chiffrement de paramètre

Avant que le Fournisseur de données Microsoft .NET pour SQL Server n’envoie une requête paramétrable à SQL Server, celui-ci demande à SQL Server (en appelant sys.sp_describe_parameter_encryption) d’analyser l’instruction de requête et de fournir des informations sur les paramètres de requête à chiffrer. Une instance de SQL Server compromise pourrait induire en erreur le fournisseur de données Microsoft .NET pour SQL Server en envoyant les métadonnées indiquant que le paramètre ne cible pas une colonne chiffrée, bien que la colonne soit chiffrée dans la base de données. Par conséquent, le fournisseur de données Microsoft .NET pour SQL Server ne va pas chiffrer la valeur du paramètre et va l’envoyer en tant que texte en clair à l’instance SQL Server compromise.

Pour éviter ce type d’attaque, une application peut définir la propriété SqlParameter.ForceColumnEncryption du paramètre sur true. Avec ce paramètre, le fournisseur de données Microsoft .NET pour SQL Server lève une exception si les métadonnées reçues du serveur indiquent que le paramètre n’a pas besoin d’être chiffré.

Bien que l’utilisation de la propriété SqlParameter.ForceColumnEncryption contribue à durcir la sécurité, elle réduit également la transparence du chiffrement sur l’application cliente. Si vous mettez à jour le schéma de base de données pour modifier le jeu de colonnes chiffrées, vous devrez apporter des modifications à l’application.

L’exemple de code suivant illustre l’utilisation de la propriété SqlParameter.ForceColumnEncryption afin d’éviter l’envoi de numéros de sécurité sociale sous forme de texte en clair vers la base de données.

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.
    }
}

Configuration des chemins d’accès des clés principales de colonne approuvés

Les métadonnées de chiffrement renvoyées par SQL Server pour les paramètres de requête ciblant des colonnes chiffrées et pour les résultats récupérés à partir de colonnes de chiffrement, incluent le chemin d’accès des clés principales de colonne qui identifie le magasin de clés et l’emplacement de la clé dans ce dernier. Si l’instance est compromise, celle-ci pourrait envoyer le chemin d’accès des clés dirigeant le Fournisseur de données Microsoft .NET pour SQL Server vers l’emplacement contrôlé par un attaquant. Ce processus peut entraîner une fuite des informations d’identification du magasin de clés, quand le magasin de clés exige l’authentification de l’application.

Pour empêcher ces attaques, l’application peut spécifier la liste des chemins d’accès des clés approuvés pour un serveur donné en utilisant la propriété SqlConnection.ColumnEncryptionTrustedMasterKeyPaths. Si le Fournisseur de données Microsoft .NET pour SQL Server reçoit un chemin d’accès des clés ne figurant pas dans la liste des chemins d’accès de clé approuvés, il lève une exception.

Bien que la définition de chemins de clé approuvés améliore la sécurité de votre application, vous devez changer le code ou/et la configuration de l’application chaque fois que vous permutez votre clé principale de colonne (à chaque changement du chemin de la clé principale de colonne).

L’exemple suivant montre comment configurer les chemins d’accès des clés principales de colonne approuvés :

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

Copie de données chiffrées à l’aide de SqlBulkCopy

Grâce à SqlBulkCopy, les données qui sont déjà chiffrées et stockées dans une table peuvent être copiées vers une autre table, sans que vous ayez à les déchiffrer. Pour ce faire :

  • Vérifiez que la configuration du chiffrement de la table cible est identique à celle de la table source. Les deux tables doivent avoir les mêmes colonnes chiffrées, et ces colonnes doivent être chiffrées à l’aide des mêmes types et des mêmes clés de chiffrement. Si les colonnes cibles sont chiffrées différemment de la colonne source correspondante, vous ne pourrez pas déchiffrer les données de la table cible après les avoir copiées. Les données seront endommagées.
  • Configurez les deux connexions de base de données, c’est-à-dire celle vers la table source et celle vers la table cible, sans activer Always Encrypted.
  • Définissez l’option AllowEncryptedValueModifications (consultez SqlBulkCopyOptions).

Notes

Utilisez AllowEncryptedValueModifications avec précaution. Ce paramètre expose la base de données à un risque d’endommagement, car le Fournisseur de données Microsoft .NET pour SQL Server ne vérifie pas que les données sont chiffrées ni qu’elles sont correctement chiffrées avec le même type de chiffrement, le même algorithme et la même clé que ceux de la colonne cible.

Voici un exemple qui copie les données d’une table à une autre. Les colonnes SSN et BirthDate sont censées être chiffrées.

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

Référence de l’API Chiffrement intégral

Namespace :Microsoft.Data.SqlClient

Assembly : Microsoft.Data.SqlClient.dll

Nom Description
Classe SqlColumnEncryptionCertificateStoreProvider Un fournisseur de magasin de clés pour le magasin de certificats Windows.
Classe SqlColumnEncryptionCngProvider Un fournisseur de magasin de clés pour l’API de chiffrement Microsoft : de la prochaine génération (CNG).
Classe SqlColumnEncryptionCspProvider Un fournisseur de magasin de clés pour les fournisseurs de services de chiffrement (CSP) basés sur la CAPI Microsoft.
classe SqlColumnEncryptionKeyStoreProvider Classe de base des fournisseurs de magasins de clés.
Énumération SqlCommandColumnEncryptionSetting Paramètres pour contrôler le comportement d’Always Encrypted pour des requêtes individuelles.
Énumération SqlConnectionAttestationProtocol Spécifie une valeur pour le protocole d’attestation lors de l’utilisation d’Always Encrypted avec des enclaves sécurisées
Énumération SqlConnectionColumnEncryptionSetting Paramètres pour activer le chiffrement et le déchiffrement d’une connexion de base de données.
Propriété SqlConnectionStringBuilder.ColumnEncryptionSetting Obtient et définit Always Encrypted dans la chaîne de connexion.
propriété SqlConnection.ColumnEncryptionQueryMetadataCacheEnabled Active et désactive la mise en cache des métadonnées de requête de chiffrement.
SqlConnection.ColumnEncryptionKeyCacheTtl Obtient et définit la durée de vie des entrées du cache de clé de chiffrement de colonne.
SqlConnection.ColumnEncryptionTrustedMasterKeyPaths Vous permet de définir une liste de chemins d’accès de clés approuvés pour un serveur de bases de données. Si, pendant le traitement d’une requête d’application, le pilote reçoit un chemin d’accès de clé qui ne figure pas dans la liste, la requête échoue. Cette propriété offre une protection supplémentaire contre les attaques au niveau de la sécurité durant lesquelles une machine SQL Server compromise fournit de faux chemins de clés, ce qui peut donner lieu à une fuite des informations d’identification du magasin de clés.
Méthode SqlConnection.RegisterColumnEncryptionKeyStoreProviders Vous permet d’inscrire des fournisseurs de magasins de clés personnalisés. Il s’agit d’un dictionnaire qui mappe des noms de fournisseurs de magasins de clés à des implémentations de fournisseurs de magasins de clés.
Constructeur SqlCommand (String, SqlConnection, SqlTransaction, SqlCommandColumnEncryptionSetting) Permet de contrôler le comportement d’Always Encrypted pour des requêtes individuelles.
SqlParameter.ForceColumnEncryption Applique le chiffrement d’un paramètre. Si SQL Server informe le pilote que le paramètre ne nécessite pas de chiffrement, la requête utilisant le paramètre échoue. Cette propriété offre une protection supplémentaire contre les attaques au niveau de la sécurité durant lesquelles une machine SQL Server compromise fournit des métadonnées de chiffrement incorrectes au client, ce qui peut entraîner la divulgation de données.
Mot clé de la chaîne de connexion : Column Encryption Setting=enabled Active ou désactive la fonctionnalité Always Encrypted pour la connexion.

Voir aussi