Partilhar via


Usar Always Encrypted com o Provedor de Dados Microsoft .NET para SQL Server

Aplica-se a: .NET Framework .NET .NET Standard

Este artigo fornece informações sobre como desenvolver aplicações .NET usando Always Encrypted ou Always Encrypted com enclaves seguros e o Microsoft .NET Data Provider for SQL Server.

O Always Encrypted permite que aplicações clientes encriptem dados sensíveis e nunca revelem os dados ou as chaves de encriptação ao SQL Server ou Azure SQL Database. Um driver habilitado para Always Crypted, como o Microsoft .NET Data Provider for SQL Server, alcança esta segurança ao encriptar e desencriptar de forma transparente dados sensíveis na aplicação cliente. O driver determina automaticamente quais os parâmetros de consulta que correspondem a colunas sensíveis da base de dados (protegidas com o Always Encrypted) e encripta os valores desses parâmetros antes de passar os dados para o servidor. De forma semelhante, o driver desencripta de forma transparente os dados recuperados das colunas da base de dados encriptadas nos resultados das consultas. Para mais informações, consulte Desenvolver aplicações usando Always Encrypted e Desenvolver aplicações usando Always Encrypted com enclaves seguros.

Pré-requisitos

  • Configure Sempre Encriptado na sua base de dados. Este processo envolve o provisionamento de chaves Always Encrypted e a configuração de encriptação para colunas selecionadas da base de dados. Se ainda não tiver uma base de dados com o Always Encrypted configurado, siga as instruções no Tutorial: Como começar com o Always Encrypted.
  • Se estiver a usar Always Encrypted com enclaves seguros, consulte Desenvolver aplicações usando Always Encrypted com enclaves seguros para mais pré-requisitos.
  • Garante que a plataforma .NET necessária está instalada na tua máquina de desenvolvimento. Com o Microsoft.Data.SqlClient, a funcionalidade Always Encrypted é suportada tanto para o .NET Framework como para o .NET Core. Certifique-se de que o .NET Framework 4.6 ou superior, ou o .NET Core 2.1 ou superior, está configurado como a versão alvo da plataforma .NET no seu ambiente de desenvolvimento. Com o Microsoft.Data.SqlClient versão 2.1.0 e superiores, a funcionalidade Always Encrypted também é suportada para o .NET Standard 2.0. Para usar o Always Encrypted com enclaves seguros, é necessário o .NET Standard 2.1 . Se quiser usar enclaves VBS sem atestado, é necessário o Microsoft.Data.SqlClient versão 4.1 ou superior. Se estiver a utilizar o Visual Studio, consulte a visão geral do direcionamento do Framework.

A tabela seguinte resume as plataformas .NET necessárias para utilizar o Always Encrypted com Microsoft.Data.SqlClient.

Suporte a Always Encrypted Suporte para Always Encrypted com Secure Enclave Plataforma de Destino Versão Microsoft.Data.SqlClient Sistema Operativo
Yes Yes .NET Framework 4.6+ 1.1.0+ Windows
Yes Yes .NET Core 2.1+ 2.1.0+1 Windows, Linux, macOS
Yes Não .NET Padrão 2.0 2.1.0+ Windows, Linux, macOS
Yes Yes .NET Standard 2.1+ 2.1.0+ Windows, Linux, macOS

Observação

1 Antes do Microsoft.Data.SqlClient versão 2.1.0, o Always Encrypted só é suportado no Windows.

Ativar o Always Encrypted para consultas de aplicações

A forma mais fácil de ativar a encriptação dos parâmetros e a desencriptação dos resultados das consultas direcionadas a colunas encriptadas é definir o valor da Column Encryption Setting palavra-chave da string de ligação para ativado.

O exemplo seguinte utiliza uma cadeia de ligação que permite o Always Encrypted:

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

O seguinte excerto de código é um exemplo equivalente usando a propriedade 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();

O Always Encrypted também pode ser ativado para consultas individuais. Consulte a secção Controlar o impacto no desempenho do Sempre Encriptado abaixo. Ativar o Always Encrypted não é suficiente para que a encriptação ou a desencriptação tenham sucesso. Também precisa de garantir que:

  • A aplicação tem as permissões VIEW ANY COLUMN MASTER KEY DEFINITION e VIEW ANY COLUMN ENCRYPTION KEY DEFINITION (definição de chave de criptografia de colunas), necessárias para aceder aos metadados sobre chaves Always Encrypted na base de dados. Para mais detalhes, consulte a secção Permissões de Base de Dados em Always Encrypted (Database Engine).
  • A aplicação pode aceder à chave mestra da coluna que protege as chaves de encriptação das colunas, que encriptam as colunas da base de dados consultadas.

Ativar o Always Encrypted com enclaves seguros

A partir do Microsoft.Data.SqlClient versão 1.1.0, o driver suporta o Always Encrypted com enclaves seguros.

Para informações gerais sobre o desenvolvimento de aplicações usando enclaves, consulte Desenvolver aplicações usando Always Encrypted com enclaves seguros.

Para permitir cálculos de enclave para uma ligação a uma base de dados, deve definir as seguintes palavras-chave de cadeia de ligação, além de ativar o Always Encrypted (como explicado na secção anterior):

  • Attestation Protocol - especifica um protocolo de atestado.

    • Se esta palavra-chave não for especificada, os enclaves seguros ficam desativados na ligação.
    • Se estiver a usar SQL Server com enclaves de segurança baseados em virtualização (VBS) e Host Guardian Service (HGS), o valor desta palavra-chave deve ser HGS.
    • Se estiver a usar Azure SQL Database com enclaves Intel SGX e Microsoft Azure Attestation, o valor desta palavra-chave deve ser AAS.
    • Se estiver a usar Azure SQL Database ou SQL Server com enclaves VBS e quiser abdicar da atistação, o valor desta palavra-chave deve ser None. Requer a versão 4.1 ou superior.

    Observação

    'Nenhum' (sem atestado) é a única opção atualmente suportada para enclaves VBS no Azure SQL Database.

  • Enclave Attestation URL - especifica uma URL de atestação (um endpoint de serviço de atestação). Precisa de obter um URL de atestação para o seu ambiente junto ao administrador do serviço de atestação.

Para um tutorial passo a passo, veja Tutorial: Desenvolver uma aplicação .NET usando Always Encrypted com enclaves seguros.

Recuperação e modificação de dados em colunas encriptadas

Depois de ativar o Always Encrypted para consultas de aplicação, pode usar APIs padrão do SqlClient (ver Recuperar e Modificar Dados em ADO.NET) ou as APIs Microsoft .NET Data Provider for SQL Server , definidas no espaço de nomes Microsoft.Data.SqlClient, para recuperar ou modificar dados em colunas de base de dados encriptadas. Se a sua aplicação tiver as permissões necessárias para a base de dados e puder aceder à chave mestra da coluna, o Microsoft .NET Data Provider para SQL Server encriptará quaisquer parâmetros de consulta que visem colunas encriptadas e descifrará dados recuperados de colunas encriptadas, retornando valores em texto simples dos tipos .NET correspondentes aos tipos de dados do SQL Server definidos para as colunas no esquema da base de dados. Se o Always Encrypted não estiver ativado, consultas com parâmetros que visam colunas encriptadas falharão. As consultas ainda podem recuperar dados de colunas encriptadas desde que a consulta não tenha parâmetros direcionados a colunas encriptadas. No entanto, o Microsoft .NET Data Provider para SQL Server não tenta desencriptar quaisquer valores recuperados de colunas encriptadas e a aplicação recebe dados binários encriptados (como arrays de bytes).

A tabela seguinte resume o comportamento das consultas, dependendo se o Always Encrypted está ativado ou não:

Característica da consulta O Always Encrypted está ativado e a aplicação pode aceder às chaves e metadados das chaves O Always Encrypted está ativado e a aplicação não consegue aceder às chaves nem aos metadados das chaves O Always Encrypted está desativado
Consultas com parâmetros direcionados a colunas encriptadas. Os valores dos parâmetros são encriptados de forma transparente. Erro Erro
Consultas que recuperam dados de colunas encriptadas sem parâmetros que visem colunas encriptadas. Os resultados das colunas encriptadas são desencriptados de forma transparente. A aplicação recebe valores de texto simples dos tipos de dados .NET correspondentes aos tipos SQL Server configurados para as colunas encriptadas. Erro Os resultados das colunas encriptadas não são desencriptados. A aplicação recebe valores encriptados como arrays de bytes (byte[]).

Os exemplos seguintes ilustram a recuperação e modificação de dados em colunas encriptadas. Os exemplos assumem a tabela alvo com o esquema abaixo. As SSN colunas e BirthDate estão encriptadas.

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

Exemplo de inserção de dados

Este exemplo insere uma linha na tabela de Pacientes. Observe os seguintes detalhes:

  • Não há nada específico sobre encriptação no código de exemplo. O Microsoft .NET Data Provider para SQL Server deteta e encripta automaticamente os parâmetros paramSSN e paramBirthdate que visam colunas encriptadas. Este comportamento torna a encriptação transparente para a aplicação.
  • Os valores inseridos nas colunas da base de dados, incluindo as colunas encriptadas, são passados como objetos SqlParameter . Embora usar o SqlParameter seja opcional ao enviar valores para colunas não encriptadas (embora seja altamente recomendado porque ajuda a prevenir a injeção SQL), é obrigatório para valores que visam colunas encriptadas. Se os valores inseridos nas SSN colunas ou BirthDate fossem passados como literais embutidos na instrução de consulta, a consulta falharia porque o Microsoft .NET Data Provider para SQL Server não conseguiria determinar os valores nas colunas encriptadas de destino, por isso não encriptaria os valores. Como resultado, o servidor rejeitava-as por serem incompatíveis com as colunas encriptadas.
  • O tipo de dado do parâmetro direcionado à SSN coluna é definido para uma cadeia ANSI (não-Unicode), que corresponde ao tipo de dados char/varchar SQL Server. Se o tipo do parâmetro fosse definido para uma string Unicode (String), que mapeia para nchar/nvarchar, a consulta falharia, pois o Always Encrypted não suporta conversões de valores nchar/nvarchar encriptados para valores cifrados de char/varchar. Consulte SQL Server Mapeamentos de Tipos de Dados para informações sobre os mapeamentos de tipos de dados.
  • O tipo de dado do parâmetro inserido na BirthDate coluna é explicitamente definido para o tipo de dado alvo do SQL Server usando a Propriedade SqlParameter.SqlDbType, em vez de depender do mapeamento implícito dos tipos .NET para os tipos de dados do SQL Server aplicado ao usar a Propriedade SqlParameter.DbType. Por defeito, a Estrutura de DataHora corresponde ao tipo de dado data-hora SQL Server. Como o tipo de dados da BirthDate coluna é data e o Always Encrypted não suporta a conversão de valores de data-hora encriptados para valores de data encriptados, usar o mapeamento padrão resultaria num erro.
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();
}

Exemplo de recuperação de dados em texto simples

O exemplo seguinte demonstra a filtragem de dados com base em valores encriptados e a recuperação de dados em texto simples a partir de colunas encriptadas.

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

Observação

  • O valor usado na cláusula WHERE para filtrar na SSN coluna precisa de ser passado usando o SqlParameter, para que o Microsoft .NET Data Provider para SQL Server possa encriptá-lo de forma transparente antes de o enviar para a base de dados.

  • Todos os valores impressos pelo programa serão em texto simples, pois o Microsoft .NET Data Provider para SQL Server irá desencriptar de forma transparente os dados recuperados das colunas SSN e BirthDate.

  • As consultas podem realizar comparações de igualdade em colunas se estiverem encriptadas usando encriptação determinística.

Exemplo de recuperação de dados encriptados

Se o Always Encrypted não estiver ativado, uma consulta ainda pode recuperar dados de colunas encriptadas, desde que a consulta não tenha parâmetros direcionados a colunas encriptadas.

O exemplo seguinte demonstra como recuperar dados binários encriptados de colunas encriptadas.

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

Observação

  • Como o Always Encrypted não está ativado na cadeia de ligação, a consulta devolverá valores encriptados de SSN e BirthDate como arrays de bytes (o programa converte os valores em strings).

  • Uma consulta que recupera dados de colunas encriptadas com o modo Sempre Encriptado desativado pode ter parâmetros, desde que nenhum deles tenha como alvo uma coluna encriptada. A consulta acima filtra-se por Apelido, que não está encriptado na base de dados. Se a consulta fosse filtrada por SSN ou BirthDate, a consulta falharia.

Evitar problemas comuns ao consultar colunas encriptadas

Esta secção descreve categorias comuns de erros ao consultar colunas encriptadas de aplicações .NET e algumas orientações sobre como os evitar.

Erros de conversão de tipos de dados não suportados

O Always Encrypted suporta poucas conversões para tipos de dados encriptados. Consulte Sempre Encriptado para uma lista detalhada de conversões de tipos suportadas. Faça o seguinte para evitar erros de conversão de tipos de dados:

  • Defina os tipos de parâmetros direcionados a colunas encriptadas para que o tipo de dados do parâmetro no SQL Server seja exatamente igual ao tipo da coluna de destino, ou que seja possível uma conversão do tipo de dados do SQL Server para o tipo de destino da coluna. Pode impor o mapeamento desejado dos tipos de dados .NET para tipos de dados SQL Server específicos usando a propriedade SqlParameter.SqlDbType.
  • Verificar que a precisão e escala dos parâmetros que direcionam as colunas dos tipos de dados decimais e numéricos do SQL Server são as mesmas da precisão e escala configuradas para a coluna de destino.
  • Verifique se a precisão dos parâmetros que direcionam as colunas de datetime2, datetimeoffset ou time dos tipos de dados do SQL Server não é maior do que a precisão da coluna de destino (em consultas que modificam valores na coluna de destino).

Erros devido à passagem de texto simples em vez de valores encriptados

Qualquer valor que tenha como alvo uma coluna encriptada precisa de ser encriptado dentro da aplicação. Uma tentativa de inserir/modificar ou filtrar por um valor de texto simples numa coluna encriptada resultará num erro como este:

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'

Para evitar tais erros, certifique-se:

  • O Always Encrypted está ativado para consultas de aplicação que visam colunas encriptadas (para a string de ligação ou no objeto SqlCommand para uma consulta específica).
  • Usas o SqlParameter para enviar dados direcionados a colunas encriptadas. O exemplo seguinte mostra uma consulta que filtra incorretamente por um literal/constante numa coluna encriptada (SSN) em vez de passar o literal dentro de um objeto SqlParameter.
using (SqlCommand cmd = connection.CreateCommand())
{
    cmd.CommandText = @"SELECT [SSN], [FirstName], [LastName], [BirthDate] FROM [dbo].[Patients] WHERE SSN = '795-73-9838'";
    cmd.ExecuteNonQuery();
}

Trabalhar com repositórios de chaves mestras de coluna

Para encriptar um valor de parâmetro ou desencriptar dados nos resultados da consulta, o Fornecedor de Dados Microsoft .NET para SQL Server precisa de obter uma chave de encriptação de coluna configurada para a coluna de destino. As chaves de encriptação de colunas são armazenadas em forma encriptada nos metadados da base de dados. Cada chave de encriptação de coluna tem uma chave mestra correspondente que foi usada para encriptar a chave de encriptação da coluna. Os metadados da base de dados não armazenam as chaves mestras da coluna – contêm apenas a informação sobre um armazenamento de chaves que contém uma determinada chave mestra de coluna e a localização da chave no armazenamento de chaves.

Para obter um valor em texto simples de uma chave de encriptação de coluna, o Microsoft .NET Data Provider para SQL Server obtém primeiro os metadados tanto sobre a chave de encriptação da coluna como sobre a sua correspondente chave mestra de coluna. Depois, utiliza a informação dos metadados para contactar o armazenamento de chaves que contém a chave mestra da coluna e para desencriptar a chave de encriptação da coluna encriptada. O Microsoft .NET Data Provider para SQL Server comunica com um repositório de chaves usando um provedor de chave mestra de coluna – que é uma instância de uma classe derivada da classe SqlColumnEncryptionKeyStoreProvider.

O processo para obter uma chave de encriptação de coluna:

  1. Se o Always Encrypted estiver ativado para uma consulta, o Microsoft .NET Data Provider for SQL Server chama de forma transparente o sys.sp_describe_parameter_encryption para recuperar metadados de cifragem para parâmetros direcionados a colunas cifradas, caso a consulta contenha parâmetros. Para dados encriptados contidos nos resultados de uma consulta, o SQL Server anexa automaticamente metadados de encriptação. A informação sobre a chave mestra da coluna inclui:

    • O nome de um fornecedor de repositório de chaves que encapsula um repositório de chaves contendo a chave mestra de coluna.
    • O caminho da chave que especifica a localização da chave mestra da coluna no armazenamento de chaves.

    A informação sobre a chave de encriptação da coluna inclui:

    • O valor criptografado de uma chave de criptografia de coluna.
    • O nome do algoritmo usado para encriptar a chave de encriptação da coluna.
  2. O Microsoft .NET Data Provider para SQL Server utiliza o nome do fornecedor de armazenamento de chave mestra da coluna para consultar o objeto provider, que é uma instância de uma classe derivada da classe SqlColumnEncryptionKeyStoreProvider, numa estrutura de dados interna.

  3. Para desencriptar a chave de encriptação da coluna, o Microsoft .NET Data Provider para SQL Server chama o SqlColumnEncryptionKeyStoreProvider.DecryptColumnEncryptionKey() método, passando o caminho da chave mestra da coluna, o valor encriptado da chave de encriptação da coluna e o nome do algoritmo de encriptação usado para produzir a chave de encriptação da coluna encriptada.

Utilização de fornecedores de armazenamento de chaves-mestras de coluna incorporados

O Microsoft .NET Data Provider para SQL Server vem com os seguintes fornecedores de armazenamento de chaves mestras em coluna incorporados, que são pré-registados com os nomes específicos dos fornecedores (usados para consultar o fornecedor). Estes fornecedores de armazenamento de chaves incorporados são suportados apenas no Windows.

Class Description Nome do fornecedor (consulta) Platform
Classe SqlColumnEncryptionCertificateStoreProvider Um fornecedor para a Loja de Certificados do Windows. MSSQL_CERTIFICATE_STORE Windows
Classe SqlColumnEncryptionCngProvider Um fornecedor para um armazenamento de chaves que suporta a Microsoft Cryptography API: Next Generation (CNG) API. Normalmente, um armazenamento deste tipo é um módulo de segurança de hardware – um dispositivo físico que protege e gere chaves digitais e fornece processamento criptográfico. MSSQL_CNG_STORE Windows
Classe SqlColumnEncryptionCspProvider Um fornecedor para um armazenamento de chaves que suporta a Microsoft Cryptography API (CAPI). Normalmente, um armazenamento deste tipo é um módulo de segurança de hardware – um dispositivo físico que protege e gere chaves digitais e fornece processamento criptográfico. MSSQL_CSP_PROVIDER Windows

Não precisa de fazer alterações no código da aplicação para usar estes fornecedores, mas note os seguintes detalhes:

  • Você (ou o seu DBA) precisa garantir que o nome do provedor, configurado nos metadados da chave mestra de coluna, está correto e que o caminho da chave mestra de coluna cumpre o formato de caminho válido para um determinado provedor. Recomenda-se que configure as chaves usando ferramentas como o SQL Server Management Studio, que gera automaticamente os nomes válidos dos fornecedores e caminhos das chaves ao emitir a instrução CREATE COLUMN MASTER KEY (Transact-SQL). Para mais informações, consulte Configurar Sempre Encriptado usando SQL Server Management Studio e Configurar Sempre Encriptado usando PowerShell.
  • Garanta que a sua aplicação pode aceder à chave no armazenamento de chaves. Este processo pode envolver conceder à sua aplicação acesso à chave e/ou ao armazenamento de chaves, dependendo do armazenamento de chaves, ou a realização de outros passos de configuração específicos do armazenamento de chaves. Por exemplo, para aceder a um armazenamento de chaves que implementa CNG ou CAPI (como um módulo de segurança de hardware), precisa de garantir que uma biblioteca que implemente CNG ou CAPI para a sua loja está instalada na sua máquina de aplicação. Para obter detalhes, consulte Criar e armazenar chaves mestras de coluna para Always Encrypted.

Usando o fornecedor Azure Key Vault

O Azure Key Vault é uma opção conveniente para armazenar e gerir chaves mestras de coluna para o Always Encrypted (especialmente se as suas aplicações estiverem alojadas no Azure). O Microsoft .NET Data Provider para SQL Server não inclui um fornecedor de armazenamento de chaves mestras em coluna incorporado para o Azure Key Vault, mas está disponível como um pacote NuGet (Microsoft.Data.SqLClient.AlwaysEncrypted.AzureKeyVaultProvider) que pode facilmente integrar com a sua aplicação. Para mais detalhes, consulte Always Encrypted - Proteja dados sensíveis na base de dados SQL com encriptação de dados e armazene as suas chaves de encriptação no Azure Key Vault.

Class Description Nome do fornecedor (consulta) Platform
SqlColumnEncryptionAzureKeyVaultProvider Class Fornecedor para o Azure Key Vault. AZURE_KEY_VAULT Windows, Linux, macOS

Suporte ao .NET

Versão Versão Microsoft.Data.SqlClient Plataformas .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 partir de v3.0.0, o Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider suporta as capacidades de armazenamento em cache da chave de encriptação de colunas ao registar o fornecedor usando as APIs SqlConnection.RegisterColumnEncryptionKeyStoreProvidersOnConnection ou SqlCommand.RegisterColumnEncryptionKeyStoreProvidersOnCommand.

A partir da v2.0.0, o Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider suporta as novas APIs Azure.Core e Azure.Identity para realizar autenticação com o Azure Key Vault. Uma instância de TokenCredential implementação pode agora ser passada aos SqlColumnEncryptionAzureKeyVaultProvider construtores para inicializar o objeto fornecedor Azure Key Vault.

Observação

Suporta Microsoft.Data.SqLClient.AlwaysEncrypted.AzureKeyVaultProvider tanto os Vaults como os HSMs Geridos no Azure Key Vault.

Para exemplos que demonstram encriptação/desencriptação com Azure Key Vault, veja Azure Key Vault a trabalhar com Always Encrypted e Azure Key Vault a trabalhar com Always Encrypted com enclaves seguros.

Implementação de um fornecedor personalizado de armazenamento de chaves mestras de coluna

Se quiser armazenar chaves mestras de coluna numa loja de chaves que não é suportada por um fornecedor existente, pode implementar um fornecedor personalizado estendendo a classe SqlColumnEncryptionKeyStoreProvider e registando o fornecedor usando um dos seguintes métodos:

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

Precedência do cache da chave de criptografia de coluna

Esta secção aplica-se à versão 3.0 e superior do Microsoft .NET Data Provider for SQL Server.

As chaves de encriptação de coluna (CEK) desencriptadas por fornecedores de armazenamento de chaves personalizados registados numa instância de ligação ou comando não serão armazenadas em cache pelo Microsoft .NET Data Provider for SQL Server. Os fornecedores de armazenamento de chaves personalizados devem implementar o seu próprio mecanismo de cache CEK.

A partir da versão 3.0.0 do Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider, cada instância de Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider tem a sua própria implementação de cache CEK. Quando registados numa instância de ligação ou comando, os CEKs desencriptados por uma instância de Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider serão limpos quando essa instância sair do âmbito:

class Program
{
    private static string connectionString;

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

Observação

A cache CEK implementada por fornecedores de key store personalizados será desativada pelo driver se a instância do fornecedor de key store estiver registada globalmente no driver usando o método SqlConnection.RegisterColumnEncryptionKeyStoreProviders . Qualquer implementação de cache CEK deve referenciar o valor de SqlColumnEncryptionKeyStoreProvider.ColumnEncryptionKeyCacheTtl antes de armazenar em cache um CEK e não deve armazená-lo em cache se o valor for zero. Isto evitará cache duplicado e possível confusão do utilizador ao tentar configurar a cache de chaves.

Registo de um fornecedor personalizado de armazenamento de chaves mestras de coluna

Esta secção aplica-se à versão 3.0 e superior do fornecedor.

Os provedores de armazenamento de chaves mestras personalizadas podem ser registrados com o driver em três camadas diferentes. A precedência dos três registos é a seguinte:

  • O registo por comando será verificado se não estiver vazio.
  • Se o registo por comando estiver vazio, o registo por ligação será verificado se não estiver vazio.
  • Se o registo por ligação estiver vazio, o registo global será verificado.

Uma vez encontrado qualquer fornecedor de loja-chave ao nível de registo, o condutor NÃO recorrerá aos outros registos para procurar um fornecedor. Se os prestadores estiverem registados mas o fornecedor correto não for encontrado a um determinado nível, será lançada uma exceção contendo apenas os prestadores registados no registo que foi verificado.

Os fornecedores incorporados de armazenamento de chave mestra na coluna disponíveis para a Windows Certificate Store, CNG Store e CSP estão pré-registados.

Os três níveis de registo suportam diferentes cenários ao consultar dados encriptados. O método apropriado pode ser usado para garantir que um utilizador de uma aplicação possa aceder aos dados em texto simples se conseguir fornecer a necessária chave mestra da coluna, autenticando-se contra o repositório de chaves que contém a chave mestra da coluna.

Aplicações que partilham uma instância de SqlConnection entre vários utilizadores podem querer usar SqlCommand.RegisterColumnEncryptionKeyStoreProvidersOnCommand. Cada utilizador deve registar um fornecedor de armazenamento de chaves numa instância SqlCommand antes de executar uma consulta para aceder a uma coluna encriptada. Se o fornecedor do armazenamento de chaves conseguir aceder à chave mestra da coluna necessária no armazenamento de chaves usando as credenciais fornecidas pelo utilizador, a consulta terá sucesso.

Aplicações que criam uma instância SqlConnection para cada utilizador podem querer usar SqlConnection.RegisterColumnEncryptionKeyStoreProvidersOnConnection. Os fornecedores de armazenamento de chaves registados com este método podem ser usados pela ligação para qualquer consulta que aceda a dados encriptados.

Os fornecedores de armazenamento de chaves registados usando SqlConnection.RegisterColumnEncryptionKeyStoreProviders usarão a identidade dada pela aplicação ao autenticar-se no armazenamento de chaves.

O exemplo seguinte mostra a precedência dos provedores de armazenamento de chaves-mestras de coluna personalizados registados numa instância de conexão.

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

O exemplo seguinte mostra a precedência dos fornecedores de armazenamento de chaves mestras personalizadas registados numa instância de comandos:

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

Utilização de provedores de armazenamento de chaves mestras de coluna para o provisionamento programático de chaves

Quando o Microsoft .NET Data Provider para SQL Server acede a colunas encriptadas, encontra e chama de forma transparente o fornecedor apropriado de armazenamento de chaves mestras da coluna para desencriptar as chaves de colunas. Normalmente, o seu código de aplicação normal não chama diretamente os fornecedores de armazenamento de chave mestra de coluna. Pode, no entanto, instanciar e chamar explicitamente um fornecedor para criar e gerir programaticamente chaves Always Encrypted: para gerar uma chave de encriptação de coluna encriptada e desencriptar uma chave de encriptação de coluna (por exemplo, no contexto da rotação da chave mestra da coluna). Para mais informações, consulte Visão Geral da gestão de chaves para Always Encrypted. A implementação das suas próprias ferramentas de gestão de chaves pode ser necessária apenas se utilizar um fornecedor de armazenamento de chaves personalizado. Ao usar chaves armazenadas em armazenamentos de chaves, para os quais existem fornecedores incorporados, ou no Azure Key Vault, pode usar ferramentas existentes, como SQL Server Management Studio ou PowerShell, para gerir e provisionar chaves. O exemplo abaixo ilustra a geração de uma chave de encriptação de coluna e a utilização da classe SqlColumnEncryptionCertificateStoreProvider para encriptar a chave com um certificado.

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

Controlar o impacto no desempenho do Always Encrypted

Como o Always Encrypted é uma tecnologia de encriptação do lado do cliente, a maior parte da sobrecarga de desempenho é observada do lado do cliente, não na base de dados. Para além do custo das operações de encriptação e desencriptação, outras fontes de sobrecarga de desempenho do lado do cliente são:

  • Viagens extra de ida e volta à base de dados para recolher metadados dos parâmetros de consulta.
  • Chamadas a um armazenamento de chave mestra de coluna para aceder à mesma.

Esta secção descreve as otimizações de desempenho incorporadas no Microsoft .NET Data Provider para SQL Server e como pode controlar o impacto dos dois fatores acima no desempenho.

Controlo de viagens de ida e volta para recuperar metadados para parâmetros de consulta

Se Always Encrypted estiver ativado para uma ligação, o Microsoft .NET Data Provider para SQL Server chamará por padrão o sys.sp_describe_parameter_encryption para cada consulta parametrizada, passando a declaração de consulta (sem quaisquer valores de parâmetro) para o SQL Server. sys.sp_describe_parameter_encryption analisa a instrução de consulta para descobrir se algum parâmetro precisa de ser encriptado e, em caso afirmativo, para cada um, devolve a informação relacionada com encriptação que permitirá ao Microsoft .NET Data Provider for SQL Server encriptar valores de parâmetros. O comportamento acima assegura um elevado nível de transparência para a aplicação do cliente. A aplicação (e o programador da aplicação) não precisam de saber quais consultas acedem às colunas encriptadas, desde que os valores direcionados a colunas encriptadas sejam passados para o Microsoft .NET Data Provider for SQL Server em objetos SQLParater.

Cache de metadados de consulta

O Microsoft .NET Data Provider for SQL Server armazena em cache os resultados de sys.sp_describe_parameter_encryption para cada instrução de consulta. Assim, se a mesma instrução de consulta for executada várias vezes, o driver chama sys.sp_describe_parameter_encryption apenas uma vez. A cache de metadados de encriptação para instruções de consulta reduz substancialmente o custo de desempenho da obtenção de metadados da base de dados. O cache está ativado por defeito. Pode desativar a cache de metadados de parâmetros definindo a propriedade SqlConnection.ColumnEncryptionQueryMetadataCacheEnabled como falsa, mas isso não é recomendado, exceto em casos raros como o descrito abaixo:

Considere uma base de dados que tem dois esquemas diferentes: s1 e s2. Cada esquema contém uma tabela com o mesmo nome: t. As definições das s1.t tabelas e s2.t são idênticas, exceto propriedades relacionadas com encriptação: Uma coluna, chamada c, em s1.t não está encriptada, e está encriptada em s2.t. A base de dados tem dois utilizadores: u1 e u2. O esquema padrão para os u1 utilizadores é s1. O esquema padrão para u2 é s2. Uma aplicação .NET abre duas ligações à base de dados, imitando o u1 utilizador numa ligação e o u2 utilizador noutra. A aplicação envia uma consulta com um parâmetro direcionado à c coluna sobre a ligação para o utilizador u1 (a consulta não especifica o esquema, por isso assume-se o esquema padrão do utilizador). Em seguida, a aplicação envia a mesma consulta através da ligação para o u2 utilizador. Se a cache de metadados de consulta estiver ativada, após a primeira consulta, a cache será preenchida com metadados que indicam que a c coluna que o parâmetro de consulta direciona não está encriptada. Como a segunda consulta tem a mesma instrução de consulta, a informação armazenada na cache será utilizada. Como resultado, o driver enviará a consulta sem encriptar o parâmetro (o que está incorreto, pois a coluna alvo, s2.t.c, está encriptada), libertando o valor em texto simples do parâmetro para o servidor. O servidor irá detetar essa incompatibilidade e forçar o driver a atualizar a cache, para que a aplicação reenvie a consulta de forma transparente com o valor do parâmetro corretamente encriptado. Nesse caso, a cache deve ser desativada para evitar a fuga de valores sensíveis para o servidor.

Definição: Sempre Encriptado ao nível da consulta

Para controlar o impacto de desempenho da obtenção de metadados de encriptação para consultas parametrizadas, pode ativar o Always Encrypted para consultas individuais, em vez de configurar para a ligação. Desta forma, pode garantir que sys.sp_describe_parameter_encryption é invocado apenas para consultas que sabe que têm parâmetros direcionados a colunas encriptadas. Note, no entanto, que ao fazê-lo, reduz a transparência da encriptação: se alterar as propriedades de encriptação das colunas da sua base de dados, pode ser necessário alterar o código da sua aplicação para o alinhar com as alterações do esquema.

Observação

Definir Sempre Encriptado ao nível da consulta limita o benefício de desempenho da cache de metadados de encriptação de parâmetros.

Para controlar o comportamento Always Encrypted de consultas individuais, precisa de usar este construtor de SqlCommand e SqlCommandColumnEncryptionSetting. Aqui ficam algumas orientações úteis:

  • Se a maioria das consultas que uma aplicação cliente executa forem para aceder a colunas encriptadas:
    • Defina a palavra-chave da cadeia de conexão Column Encryption Setting para Enabled.
    • Defina o SqlCommandColumnEncryptionSetting para Desabilitado para consultas individuais que não acedam a nenhuma coluna encriptada. Esta configuração desativa tanto a chamada sys.sp_describe_parameter_encryption como a tentativa de desencriptar quaisquer valores no conjunto de resultados.
    • Defina SqlCommandColumnEncryptionSetting para ResultSetOnly para consultas individuais que não tenham parâmetros que exijam encriptação, mas que recuperam dados de colunas encriptadas. Esta configuração irá desativar a chamada de sys.sp_describe_parameter_encryption e a encriptação de parâmetros. A consulta será capaz de desencriptar os resultados das colunas de encriptação.
  • Se a maioria das consultas que uma aplicação cliente executa não acede às colunas encriptadas:
    • Defina a palavra-chave da cadeia de ligação Column Encryption Setting para Desabilitada.
    • Defina o SqlCommandColumnEncryptionSetting para Ativado para consultas individuais que tenham quaisquer parâmetros que precisem de ser encriptados. Esta configuração permitirá tanto chamar sys.sp_describe_parameter_encryption como a desencriptação de quaisquer resultados de consulta recuperados de colunas encriptadas.
    • Defina SqlCommandColumnEncryptionSetting para ResultSetOnly para consultas que não tenham parâmetros que exijam encriptação, mas que recuperam dados de colunas encriptadas. Esta configuração irá desativar a chamada de sys.sp_describe_parameter_encryption e a encriptação de parâmetros. A consulta será capaz de desencriptar os resultados das colunas de encriptação.

No exemplo abaixo, o Always Encrypted está desativado para a ligação à base de dados. A consulta que a aplicação emite tem um parâmetro que direciona a coluna Último Nome que não está encriptada. A consulta recupera dados das SSN colunas e BirthDate que estão ambas encriptadas. Nesse caso, não é necessário chamar sys.sp_describe_parameter_encryption para recuperar metadados de encriptação. No entanto, é necessário ativar a desencriptação dos resultados da consulta, para que a aplicação possa receber valores de texto simples das duas colunas encriptadas. A definição ResultSetOnlydo SqlCommandColumnEncryptionSetting é usada para garantir isso.

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

Cache de chaves de encriptação de colunas

Para reduzir o número de chamadas para um armazenamento de chaves mestras de coluna para decifrar chaves de encriptação de coluna, o Microsoft .NET Data Provider para SQL Server armazena em cache as chaves de encriptação de colunas em formato de texto simples na memória. Depois de o fornecedor receber o valor da chave de encriptação da coluna encriptada dos metadados da base de dados, o driver tenta primeiro encontrar a chave de encriptação da coluna em texto simples correspondente ao valor da chave encriptada. O driver chama o repositório de chaves que contém a chave mestra da coluna apenas se não conseguir encontrar o valor da chave de encriptação da coluna cifrada na cache.

As entradas de cache são removidas após um intervalo de tempo de vida configurável por motivos de segurança. O valor padrão de tempo de vida é de 2 horas. Se tiver requisitos de segurança mais rigorosos sobre quanto tempo as chaves de encriptação das colunas podem ser armazenadas em cache em texto simples na aplicação, pode alterá-las usando a propriedade SqlConnection.ColumnEncryptionKeyCacheTtl.

Os fornecedores de armazenamento de chaves personalizados registados usando SqlConnection.RegisterColumnEncryptionKeyStoreProvidersOnConnection e SqlCommand.RegisterColumnEncryptionKeyStoreProvidersOnCommand não terão as suas chaves de encriptação de coluna desencriptadas em cache pelo Microsoft .NET Data Provider for SQL Server. Em vez disso, os fornecedores de armazenamento de chaves personalizados devem implementar o seu próprio mecanismo de cache. Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider A versão 3.0.0 e superiores vêm com a sua própria implementação de cache.

Para suportar cenários em que diferentes utilizadores da mesma aplicação podem executar múltiplas consultas, fornecedores personalizados de armazenamento de chaves podem ser mapeados para um utilizador e registados numa instância de ligação ou comando específica para esse utilizador. O exemplo seguinte mostra como uma instância de Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider pode ser reutilizada entre diferentes SqlCommand objetos para o mesmo utilizador. A sua cache de chave de encriptação em coluna persistirá em múltiplas consultas, reduzindo o número de idas e voltas ao armazenamento de chaves:

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

Permitir proteção extra para um SQL Server comprometido

Por predefinição, o Microsoft .NET Data Provider para SQL Server depende do sistema de gestão de base de dados (SQL Server ou Azure SQL Database) para fornecer metadados sobre quais são as colunas da base de dados que são encriptadas e como. Os metadados de encriptação permitem ao Microsoft .NET Data Provider para SQL Server encriptar parâmetros de consulta e desencriptar resultados de consulta sem qualquer intervenção da aplicação, o que reduz significativamente o número de alterações necessárias na aplicação. No entanto, se o processo do SQL Server for comprometido e um atacante manipular os metadados que o SQL Server envia para o Microsoft .NET Data Provider for SQL Server, o atacante poderá conseguir roubar informações sensíveis. Esta secção descreve APIs que ajudam a fornecer um nível extra de proteção contra este tipo de ataque, ao custo de uma transparência reduzida.

Forçar a Encriptação dos Parâmetros

Antes de o Microsoft .NET Data Provider for SQL Server enviar uma consulta parametrizada para o SQL Server, pede ao SQL Server ( chamando sys.sp_describe_parameter_encryption) que analise a instrução da consulta e forneça informações sobre quais os parâmetros da consulta que devem ser encriptados. Uma instância SQL Server comprometida pode induzir em erro o Microsoft .NET Data Provider for SQL Server ao enviar metadados que indicam que o parâmetro não tem como alvo uma coluna encriptada, mesmo que a coluna esteja encriptada na base de dados. Como resultado, o Microsoft .NET Data Provider para SQL Server não encriptava o valor do parâmetro e enviava-o como texto simples para a instância do SQL Server comprometida.

Para evitar tal ataque, uma aplicação pode definir a propriedade SqlParameter.ForceColumnEncryption para o parâmetro como true. Esta configuração fará com que o Microsoft .NET Data Provider for SQL Server lance uma exceção, se os metadados recebidos do servidor indicarem que o parâmetro não precisa de ser encriptado.

Embora a utilização da propriedade SqlParameter.ForceColumnEncryption ajude a melhorar a segurança, também reduz a transparência da encriptação para a aplicação cliente. Se atualizar o esquema da base de dados para alterar o conjunto de colunas encriptadas, poderá ser necessário fazer alterações à aplicação também.

O exemplo de código seguinte ilustra o uso da propriedade SqlParameter.ForceColumnEncryption para impedir que números de segurança social sejam enviados em texto simples para a base de dados.

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

Configuração dos caminhos de confiança para a chave mestra de coluna

Os metadados de encriptação que o SQL Server devolve para parâmetros de consulta direcionados a colunas encriptadas e para os resultados obtidos das colunas de encriptação incluem o caminho da chave mestra da coluna que identifica o armazenamento de chaves e a localização da chave no armazenamento de chaves. Se a instância do SQL Server for comprometida, pode enviar o caminho da chave que direciona o Microsoft .NET Data Provider for SQL Server para o local controlado por um atacante. Este processo pode levar à fuga de credenciais da loja-chave, se a loja exigir que a aplicação se autentice.

Para prevenir tais ataques, a aplicação pode especificar a lista de caminhos de chave de confiança para um dado servidor usando a propriedade SqlConnection.ColumnEncryptionTrustedMasterKeyPaths. Se o Microsoft .NET Data Provider para SQL Server receber um caminho de chave fora da lista de caminhos de chaves de confiança, irá lançar uma exceção.

Embora definir caminhos de chaves confiáveis melhore a segurança da sua aplicação, terá de alterar o código e/ou a configuração da aplicação sempre que rodar a sua chave mestra de coluna (sempre que o caminho da chave mestra da coluna mudar).

O exemplo seguinte mostra como configurar caminhos para chaves mestras de colunas confiáveis.

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

Cópia de dados encriptados usando SqlBulkCopy

Com o SqlBulkCopy, pode copiar dados, que já estão encriptados e armazenados numa tabela, para outra tabela, sem ter de desencriptar os dados. Para isso:

  • Certifique-se de que a configuração de encriptação da tabela de destino é idêntica à configuração da tabela de origem. Em particular, ambas as tabelas devem ter as mesmas colunas encriptadas, e as colunas devem ser encriptadas usando os mesmos tipos de encriptação e as mesmas chaves de encriptação. Se alguma das colunas de destino estiver encriptada de forma diferente da correspondente coluna de origem, não será possível desencriptar os dados na tabela de destino após a operação de cópia. Os dados serão corrompidos.
  • Configure as ligações à base de dados para a tabela de origem e para a tabela de destino sem o Always Encrypted ativado.
  • Defina a AllowEncryptedValueModifications opção (veja SqlBulkCopyOptions).

Observação

Tenha cuidado ao especificar AllowEncryptedValueModifications. Esta configuração pode levar a corromper a base de dados porque o Microsoft .NET Data Provider para SQL Server não verifica se os dados estão realmente encriptados, ou se estão corretamente encriptados usando o mesmo tipo de encriptação, algoritmo e chave da coluna de destino.

Aqui está um exemplo que copia dados de uma tabela para outra. Assume-se que as SSN colunas e BirthDate estão encriptadas.

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

Referência da API sempre criptografada

Namespace:Microsoft.Data.SqlClient

Assembly: Microsoft.Data.SqlClient.dll

Nome Description
Classe SqlColumnEncryptionCertificateStoreProvider Um fornecedor de repositório de chaves para o Repositório de Certificados do Windows.
Classe SqlColumnEncryptionCngProvider Um fornecedor de repositório de chaves para a API de Criptografia Microsoft: Próxima Geração (CNG).
Classe SqlColumnEncryptionCspProvider Um fornecedor de armazenamento de chaves para os Fornecedores de Serviços Criptográficos (CSP) baseados em Microsoft CAPI.
Classe SqlColumnEncryptionKeyStoreProvider Classe base dos fornecedores de armazenamento de chaves.
Enumeração SqlCommandColumnEncryptionSetting Definições para controlar o comportamento do Always Encrypted para consultas individuais.
Enumeração do protocolo SqlConnectionAttestationProtocol Especifica um valor para o Protocolo de Atestação ao usar o Always Encrypted com enclaves seguros
Enumeração de Configuração SqlConnectionColumnEncryptionSetting Definições para ativar encriptação e desencriptação para uma ligação a uma base de dados.
Propriedade Configuração SqlConnectionStringBuilder.ColumnEncryptionSetting Obtém e define Always Encrypted na cadeia de ligação.
Propriedade SqlConnection.ColumnEncryptionQueryMetadataCacheEnabled Ativa e desativa o cache de metadados de consultas de encriptação.
Propriedade SqlConnection.ColumnEncryptionKeyCacheTtl Obtém e define o time-to-live para entradas na cache de chave de encriptação da coluna.
Propriedade SqlConnection.ColumnEncryptionTrustedMasterKeyPaths Permite definir uma lista de caminhos de chaves de confiança para um servidor de base de dados. Se, durante o processamento de uma consulta de aplicação, o driver receber um caminho de chave que não está na lista, a consulta falhará. Esta propriedade oferece proteção extra contra ataques de segurança que envolvem um SQL Server comprometido a fornecer caminhos de chave falsos, o que pode levar à fuga de credenciais do armazenamento de chaves.
Método SqlConnection.RegisterColumnEncryptionKeyStoreProviders Permite registar fornecedores de lojas de chaves personalizadas. É um dicionário que mapeia nomes de fornecedores de armazenamento de chaves para implementações de fornecedores de armazenamento de chaves.
Construtor SqlCommand (String, SqlConnection, SqlTransaction, SqlCommandColumnEncryptionSetting) Permite-lhe controlar o comportamento do Always Encrypted para consultas individuais.
Propriedade SqlParameter.ForceColumnEncryption Aplica a encriptação de um parâmetro. Se o SQL Server informar o driver de que o parâmetro não precisa de ser encriptado, a consulta que utiliza o parâmetro falhará. Esta propriedade oferece proteção extra contra ataques de segurança que envolvam um SQL Server comprometido a fornecer metadados de encriptação incorretos ao cliente, o que pode levar à divulgação de dados.
palavra-chave de string de ligação : Column Encryption Setting=enabled Ativa ou desativa a funcionalidade Always Encrypted para a ligação.

Consulte também