Como usar o recurso Always Encrypted com o ODBC Driver for SQL Server

Baixar driver ODBC

Aplicável a

  • Driver ODBC 13.1+ para SQL Server

Introdução

Este artigo fornece informações sobre como desenvolver aplicativos ODBC usando o Always Encrypted (Mecanismo de Banco de Dados) ou o Always Encrypted com enclaves seguros e o Driver ODBC para SQL Server.

O Always Encrypted permite que os aplicativos cliente criptografem dados confidenciais e nunca revelem os dados nem as chaves de criptografia para o SQL Server ou o Banco de Dados SQL do Azure. Um driver habilitado para Always Encrypted, como o ODBC Driver for SQL Server, atinge essa segurança criptografando e descriptografando de modo transparente dados confidenciais no aplicativo cliente. O driver determina automaticamente quais parâmetros de consulta correspondem às colunas de banco de dados confidenciais (protegidas com o Always Encrypted) e criptografa os valores desses parâmetros antes de passar os dados para o SQL Server ou o Banco de Dados SQL do Azure. Da mesma forma, o driver descriptografa de modo transparente os dados recuperados das colunas de banco de dados criptografadas nos resultados da consulta. O Always Encrypted com enclaves seguras estende esse recurso para habilitar funcionalidades mais avançadas em dados confidenciais mantendo a confidencialidade desses dados.

Para obter mais informações, confira Always Encrypted (Mecanismo de Banco de Dados) e Always Encrypted com enclaves seguros.

Pré-requisitos

Configure o Sempre Criptografado em seu banco de dados. Esse processo envolve o provisionamento de chaves do Always Encrypted e a configuração de criptografia de colunas de banco de dados selecionadas. Se você ainda não tem um banco de dados com Always Encrypted configurado, siga as instruções em Tutorial: introdução ao Always Encrypted. Em particular, seu banco de dados deve conter as definições de metadados para uma chave mestra de coluna (CMK), uma chave de criptografia de coluna (CEK) e uma tabela contendo uma ou mais colunas criptografadas usando esse CEK.

Se você estiver usando o Always Encrypted com enclaves seguros, confira Desenvolver aplicativos usando o Always Encrypted com enclaves seguros para obter mais pré-requisitos.

Habilitar o Always Encrypted em um aplicativo do ODBC

A maneira mais fácil de habilitar a criptografia de parâmetro e a descriptografia de colunas criptografadas do conjunto de resultados é definindo o valor da palavra-chave da cadeia de conexão ColumnEncryption como Habilitado. Confira o seguinte exemplo de uma cadeia de conexão que habilita o Always Encrypted:

SQLWCHAR *connString = L"Driver={ODBC Driver 18 for SQL Server};Server={myServer};Encrypt=yes;Trusted_Connection=yes;ColumnEncryption=Enabled;";

O Always Encrypted pode também ser habilitado na configuração de DSN, usando a mesma chave e valor (que serão substituídos pela configuração de cadeia de conexão, se presente) ou por meio de programação com o atributo de pré-conexão SQL_COPT_SS_COLUMN_ENCRYPTION. Defini-lo dessa maneira substitui o valor definido na cadeia de conexão ou DSN:

 SQLSetConnectAttr(hdbc, SQL_COPT_SS_COLUMN_ENCRYPTION, (SQLPOINTER)SQL_COLUMN_ENCRYPTION_ENABLE, 0);

Uma vez habilitado para a conexão, o comportamento do Always Encrypted pode ser ajustado para consultas individuais. Para obter mais informações, confira Controle do impacto sobre o desempenho do Always Encrypted abaixo.

Habilitar o Always Encrypted não é suficiente para o êxito da criptografia ou descriptografia. Do mesmo modo, é preciso ter certeza de que:

  • O aplicativo tem as permissões de banco de dados VIEW ANY COLUMN MASTER KEY DEFINITION e VIEW ANY COLUMN ENCRYPTION KEY DEFINITION, necessárias para acessar os metadados das chaves Always Encrypted no banco de dados. Para saber mais, confira Permissões de banco de dados.

  • O aplicativo pode acessar a CMK que protege as CEKs das colunas criptografadas consultadas. Esse comportamento é dependente do provedor do repositório de chaves que armazena o CMK. Para saber mais, confira Como trabalhar com repositórios de chaves mestras de coluna.

Como habilitar o Always Encrypted com enclaves seguros

Observação

No Linux e no macOS, o OpenSSL versão 1.0.1 ou posterior deve usar o Always Encrypted com enclaves seguros.

A partir da versão 17.4, o driver é compatível com Always Encrypted com enclaves seguros. Para habilitar o uso do enclave ao se conectar a um banco de dados, defina a chave do DSN ColumnEncryption, a palavra-chave da cadeia de conexão ou o atributo de conexão com o seguinte valor: <attestation protocol>,<attestation URL>, em que:

  • <attestation protocol> – especifica um protocolo usado para atestado de enclave.

    • Se você estiver usando SQL Server e o HGS (Serviço Guardião de Host), <attestation protocol> deverá ser VBS-HGS.
    • Se você estiver usando Banco de Dados SQL do Azure e o Atestado do Microsoft Azure, <attestation protocol> deverá ser SGX-AAS.
    • Se você não precisar de atestado, <attestation-protocol> deverá ser VBS-NONE. (Versão 18.1 e mais recente)
  • <attestation URL> - especifica uma URL de atestação (um endpoint de serviço de atestação). Você precisará obter uma URL de atestado para seu ambiente do administrador de serviços de atestado.

Exemplos de cadeias de conexão que habilitam computações de enclave para uma conexão de banco de dados:

  • SQL Server:

    "Driver=ODBC Driver 18 for SQL Server;Server=myServer.myDomain;Encrypt=yes;Database=myDataBase;Trusted_Connection=Yes;ColumnEncryption=VBS-HGS,http://myHGSServer.myDomain/Attestation"
    
  • Banco de Dados SQL do Azure:

    "Driver=ODBC Driver 18 for SQL Server;Server=myServer.database.windows.net;Database=myDataBase;Uid=myUsername;Pwd=<password>;Encrypt=yes;ColumnEncryption=SGX-AAS,https://myAttestationProvider.uks.attest.azure.net/"
    
  • Sem atestado (v18.1 e mais recente):

    "Driver=ODBC Driver 18 for SQL Server;Server=myServer.database.windows.net;Database=myDataBase;Uid=myUsername;Pwd=<password>;Encrypt=yes;ColumnEncryption=VBS-NONE"
    

Se o servidor e o serviço de atestado estiverem configurados corretamente, juntamente com as CMKs e CEKs habilitadas para uso com enclave nas colunas criptografadas, você poderá executar consultas que usam o enclave, como criptografia no local e cálculos avançados, além das funcionalidades existentes fornecidas pelo Always Encrypted. Para obter mais informações, confira Configurar o Always Encrypted com enclaves seguros.

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

Depois de habilitar o Always Encrypted em uma conexão, você pode usar APIs ODBC padrão. As APIs ODBC podem recuperar ou modificar dados em colunas de banco de dados criptografadas. Os seguintes itens de documentação podem ser úteis:

Seu aplicativo deve ter as permissões de banco de dados necessárias e deve ser capaz de acessar a chave mestra da coluna. Em seguida, o driver criptografa todos os parâmetros de consulta destinados às colunas criptografadas. O driver também descriptografa os dados recuperados de colunas criptografadas. O driver faz toda essa criptografia e descriptografia sem nenhuma assistência do seu código-fonte. Para o seu programa, é como se as colunas não estivessem criptografadas.

Se o Sempre Criptografado não estiver habilitado, as consultas com parâmetros que se destinam a colunas criptografadas falharão. Os dados ainda podem ser recuperados de colunas criptografadas, desde que a consulta não tenha parâmetros que se destinem a colunas criptografadas. No entanto, o driver não tentará descriptografar os dados, e o aplicativo receberá os dados binários criptografados (como arrays de bytes).

A tabela abaixo resume o comportamento das consultas, dependendo de se o Always Encrypted está habilitado ou não:

Característica da consulta O Always Encrypted está habilitado e o aplicativo pode acessar as chaves e os metadados da chave O Sempre Criptografado está habilitado e o aplicativo não pode acessar as chaves nem os metadados da chave O Sempre Criptografado está desabilitado
Parâmetros destinados a colunas criptografadas. Os valores de parâmetro são criptografados de modo transparente. Erro Erro
Recuperação de dados de colunas criptografadas, sem parâmetros direcionados a colunas criptografadas. Os resultados das colunas criptografadas são descriptografados de modo transparente. O aplicativo recebe valores de coluna de texto não criptografado. Erro Os resultados das colunas criptografadas não são descriptografados. O aplicativo recebe valores criptografados como matrizes de bytes.

Os exemplos a seguir ilustram como recuperar e modificar dados em colunas criptografadas. Por exemplo, considere uma tabela com o seguinte esquema. As colunas SSN e BirthDate estão criptografadas.

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 Pacientes. Observe o seguintes detalhes:

  • Não há nada específico de criptografia no código de exemplo. O driver detecta e criptografa automaticamente os valores dos parâmetros SSN e data, que têm como destino colunas criptografadas. Esse comportamento torna a criptografia transparente para o aplicativo.

  • Os valores inseridos nas colunas de banco de dados, incluindo as colunas criptografadas, são passados como parâmetros associados (consulte Função SQLBindParameter). Embora o uso de parâmetros seja opcional ao enviar valores para colunas não criptografadas (mesmo que seja altamente recomendável, pois ajuda a prevenir a injeção de SQL), ele é necessário para valores que se destinam a colunas criptografadas. Se os valores inseridos nas colunas SSN ou BirthDate foram passados como literais inseridos na instrução de consulta, a consulta falha, pois o driver não tenta criptografar ou processar os literais em consultas. Como resultado, o servidor os rejeitaria como incompatíveis com as colunas criptografadas.

  • O tipo SQL do parâmetro inserido na coluna SSN é definido como SQL_CHAR, que mapeia para o tipo de dados char do SQL Server (rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 11, 0, (SQLPOINTER)SSN, 0, &cbSSN);). Se o tipo do parâmetro foi definido como SQL_WCHAR, que é mapeado para nchar, a consulta falha, já que o Always Encrypted não dá suporte a conversões de valores nchar criptografados em valores char criptografados no lado do servidor. Confira Referência do programador ODBC – Apêndice D: Tipos de dados para obter informações sobre os mapeamentos de tipos de dados.

    SQL_DATE_STRUCT date;
    SQLLEN cbdate;   // size of date structure  

    SQLCHAR SSN[12];
    strcpy_s((char*)SSN, _countof(SSN), "795-73-9838");

    SQLWCHAR* firstName = L"Catherine";
    SQLWCHAR* lastName = L"Abel";
    SQLINTEGER cbSSN = SQL_NTS, cbFirstName = SQL_NTS, cbLastName = SQL_NTS;

    // Initialize the date structure  
    date.day = 10;
    date.month = 9;
    date.year = 1996;

    // Size of structures   
    cbdate = sizeof(SQL_DATE_STRUCT);

    SQLRETURN rc = 0;

    string queryText = "INSERT INTO [dbo].[Patients] ([SSN], [FirstName], [LastName], [BirthDate]) VALUES (?, ?, ?, ?) ";

    rc = SQLPrepare(hstmt, (SQLCHAR *)queryText.c_str(), SQL_NTS);

    //SSN
    rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 11, 0, (SQLPOINTER)SSN, 0, &cbSSN);
    //FirstName
    rc = SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WCHAR, 50, 0, (SQLPOINTER)firstName, 0, &cbFirstName);
    //LastName
    rc = SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WCHAR, 50, 0, (SQLPOINTER)lastName, 0, &cbLastName);
    //BirthDate
    rc = SQLBindParameter(hstmt, 4, SQL_PARAM_INPUT, SQL_C_TYPE_DATE, SQL_TYPE_DATE, 10, 0, (SQLPOINTER)&date, 0, &cbdate);

    rc = SQLExecute(hstmt);

Exemplo de recuperação de dados de texto não criptografado

O exemplo a seguir demonstra como filtrar dados com base em valores criptografados e recuperar dados em texto simples a partir de colunas criptografadas. Observe o seguintes detalhes:

  • O valor usado na cláusula WHERE a ser filtrado na coluna SSN precisa ser passado usando o SQLBindParameter para que o driver possa criptografá-lo de modo transparente antes de enviá-lo para o servidor.

  • Todos os valores impressos pelo programa estarão em texto não criptografado, já que o driver descriptografará de modo transparente os dados recuperados das colunas SSN e BirthDate.

Observação

As consultas poderão executar comparações de igualdade em colunas criptografadas somente se a criptografia for determinística ou se o enclave seguro estiver habilitado.

SQLCHAR SSN[12];
strcpy_s((char*)SSN, _countof(SSN), "795-73-9838");

SQLWCHAR* firstName = L"Catherine";
SQLWCHAR* lastName = L"Abel";
SQLINTEGER cbSSN = SQL_NTS, cbFirstName = SQL_NTS, cbLastName = SQL_NTS;

SQLRETURN rc = 0;
string empty = "";
string queryText = "SELECT [SSN], [FirstName], [LastName], [BirthDate] " + empty +
    "FROM  [dbo].[Patients]" +
    "WHERE " +
    "[SSN] = ? ";

rc = SQLPrepare(hstmt, (SQLCHAR *)queryText.c_str(), SQL_NTS);

//SSN
rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 11, 0, (SQLPOINTER)SSN, 0, &cbSSN);

rc = SQLExecute(hstmt);
HandleDiagnosticRecord(hstmt, SQL_HANDLE_STMT, rc);

SQL_DATE_STRUCT dateVal;
SQLWCHAR firstNameVal[50];
SQLWCHAR lastNameVal[50];
SQLCHAR SSNVal[12];
SQLLEN cbdate;   // size of date structure  

int rowcount = 0;
while (SQL_SUCCEEDED(SQLFetch(hstmt)))
{
    rowcount++;
    SQLGetData(hstmt, 1, SQL_C_CHAR, &SSNVal, 11, &cbSSN);
    SQLGetData(hstmt, 2, SQL_C_WCHAR, &firstNameVal, 50, &cbFirstName);
    SQLGetData(hstmt, 3, SQL_C_WCHAR, &lastNameVal, 50, &cbLastName);
    SQLGetData(hstmt, 4, SQL_C_TYPE_DATE, &dateVal, 10, &cbdate);        
}

Exemplo de recuperação de dados de texto cifrado

Se o Always Encrypted não estiver habilitado, uma consulta ainda poderá recuperar dados de colunas criptografadas, desde que a consulta não tenha parâmetros que se destinam a colunas criptografadas.

O exemplo a seguir ilustra como recuperar dados binários criptografados de colunas criptografadas. Observe o seguintes detalhes:

  • Como o Always Encrypted não está habilitado na cadeia de conexão, a consulta retornará valores criptografados de SSN e BirthDate como matrizes de bytes (o programa converte os valores em cadeias de caracteres).
  • Uma consulta que recupera dados de colunas criptografadas com o Sempre Criptografado desabilitado pode ter parâmetros, desde que nenhum dos parâmetros se destinem a uma coluna criptografada. A consulta acima filtra por LastName, que não está criptografado no banco de dados. Se a consulta filtrar por SSN ou BirthDate, a consulta falhará.
SQLCHAR SSN[12];
strcpy_s((char*)SSN, _countof(SSN), "795-73-9838");

SQLWCHAR* firstName = L"Catherine";
SQLWCHAR* lastName = L"Abel";
SQLINTEGER cbSSN = SQL_NTS, cbFirstName = SQL_NTS, cbLastName = SQL_NTS;

SQLRETURN rc = 0;
string empty = "";
string queryText = "SELECT [SSN], [FirstName], [LastName], [BirthDate] " + empty +
    "FROM  [dbo].[Patients]" +
    "WHERE " +
    "[LastName] = ?";

rc = SQLPrepare(hstmt, (SQLCHAR *)queryText.c_str(), SQL_NTS);

//LastName
rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WCHAR, 50, 0, (SQLPOINTER)lastName, 0, &cbLastName);

rc = SQLExecute(hstmt);
HandleDiagnosticRecord(hstmt, SQL_HANDLE_STMT, rc);

SQL_DATE_STRUCT dateVal;
SQLWCHAR firstNameVal[50];
SQLWCHAR lastNameVal[50];
SQLCHAR SSNVal[12];
SQLLEN cbdate;   // size of date structure  

int rowcount = 0;
while (SQL_SUCCEEDED(SQLFetch(hstmt)))
{
    rowcount++;
    SQLGetData(hstmt, 1, SQL_C_CHAR, &SSNVal, 11, &cbSSN);
    SQLGetData(hstmt, 2, SQL_C_WCHAR, &firstNameVal, 50, &cbFirstName);
    SQLGetData(hstmt, 3, SQL_C_WCHAR, &lastNameVal, 50, &cbLastName);
    SQLGetData(hstmt, 4, SQL_C_TYPE_DATE, &dateVal, 10, &cbdate);        
}

Criptografia de Money/SmallMoney

A partir da versão 17.7 do driver, é possível usar o Always Encrypted com os tipos MONEY e SMALLMONEY. No entanto, algumas etapas extras deverão ser executadas. Ao executar uma inserção em colunas MONEY ou SMALLMONEY criptografadas, use um dos seguintes tipos C:

SQL_C_CHAR
SQL_C_WCHAR
SQL_C_SHORT
SQL_C_LONG
SQL_C_FLOAT
SQL_C_DOUBLE
SQL_C_BIT
SQL_C_TINYINT
SQL_C_SBIGINT
SQL_C_NUMERIC

e um tipo SQL de SQL_NUMERIC ou SQL_DOUBLE (ao usar esse tipo, pode haver perda de precisão).

Vinculação da variável

Ao executar uma associação da variável MONEY/SMALLMONEY em uma coluna criptografada, os seguintes campos do descritor deverão ser definidos:

// n is the descriptor record of the MONEY/SMALLMONEY parameter
// the type is assumed to be SMALLMONEY if isSmallMoney is true and MONEY otherwise

SQLHANDLE ipd = 0;
SQLGetStmtAttr(hStmt, SQL_ATTR_IMP_PARAM_DESC, (SQLPOINTER)&ipd, SQL_IS_POINTER, NULL);
SQLSetDescField(ipd, n, SQL_CA_SS_SERVER_TYPE, isSmallMoney ? (SQLPOINTER)SQL_SS_TYPE_SMALLMONEY :
                                                              (SQLPOINTER)SQL_SS_TYPE_MONEY, SQL_IS_INTEGER);

// If the variable is bound as SQL_NUMERIC, additional descriptor fields have to be set
// var is SQL_NUMERIC_STRUCT containing the value to be inserted

SQLHDESC   hdesc = NULL;
SQLGetStmtAttr(hStmt, SQL_ATTR_APP_PARAM_DESC, &hdesc, 0, NULL);
SQLSetDescField(hdesc, n, SQL_DESC_PRECISION, (SQLPOINTER)(var.precision), 0);
SQLSetDescField(hdesc, n, SQL_DESC_SCALE, (SQLPOINTER)(var.scale), 0);
SQLSetDescField(hdesc, n, SQL_DESC_DATA_PTR, &var, 0);

Evitando problemas comuns ao consultar colunas criptografadas

Esta seção descreve as categorias comuns de erros ao consultar colunas criptografadas de aplicativos ODBC e algumas diretrizes sobre como evitá-los.

Erros de conversão de tipo de dados sem suporte

O Always Encrypted dá suporte a algumas conversões de tipos de dados criptografados. Consulte Always Encrypted (Mecanismo de Banco de Dados) para ver a lista detalhada de conversões de tipos com suporte. Para evitar erros de conversão de tipo de dados, certifique-se de observar os seguintes pontos ao usar SQLBindParameter com parâmetros que se destinam a colunas criptografadas:

  • O tipo SQL do parâmetro é exatamente o mesmo que o tipo da coluna de destino, ou a conversão de um tipo SQL para o tipo da coluna é suportada.

  • A precisão e a escala dos parâmetros que se destinam a colunas dos tipos de dados decimal e numeric do SQL Server são iguais à precisão e à escala configuradas para a coluna de destino.

  • A precisão dos parâmetros que se destinam às colunas dos tipos de dados datetime2, datetimeoffset ou time do SQL Server não é maior que a precisão da coluna de destino, em consultas que modificam a coluna de destino.

Erros devido à passagem de texto sem formatação em vez de valores criptografados

Qualquer valor que se destina a uma coluna criptografada precisa ser criptografado antes de ser enviado ao servidor. Uma tentativa de inserir, modificar ou filtrar por um valor de texto não criptografado em uma coluna criptografada resultará em um erro. Para evitar esses erros, garanta que:

  • O Always Encrypted esteja habilitado (no DSN, na cadeia de conexão, antes de conectar, definindo o atributo de conexão SQL_COPT_SS_COLUMN_ENCRYPTION para uma conexão específica, ou o atributo de instrução SQL_SOPT_SS_COLUMN_ENCRYPTION para uma instrução específica).

  • Você usa o SQLBindParameter para enviar dados para colunas criptografadas. O exemplo abaixo mostra uma consulta que aplica incorretamente um filtro usando um valor literal/constante em uma coluna criptografada (SSN), em vez de passar o valor literal como argumento para SQLBindParameter.

string queryText = "SELECT [SSN], [FirstName], [LastName], [BirthDate] FROM [dbo].[Patients] WHERE SSN='795-73-9838'";

Precauções ao usar SQLSetPos e SQLMoreResults

SQLSetPos

A API SQLSetPos permite que um aplicativo atualize linhas em um conjunto de resultados usando buffers que foram associados a SQLBindCol e nos quais a linha de dados foi obtida anteriormente. Devido ao comportamento de preenchimento assimétrico dos tipos de comprimento fixo criptografados, é possível que os dados dessas colunas sejam alterados inesperadamente durante a execução de atualizações em outras colunas na linha. Com AE, os valores de caracteres de comprimento fixo serão preenchidos se o valor for menor do que o tamanho do buffer.

Para atenuar esse comportamento, use o sinalizador SQL_COLUMN_IGNORE para ignorar colunas que não serão atualizadas como parte de SQLBulkOperations e ao usar SQLSetPos para atualizações baseadas em cursor. Todas as colunas que não estão sendo modificadas diretamente pelo aplicativo devem ser ignoradas, tanto por desempenho quanto para evitar o truncamento de colunas vinculadas a um buffer menor que seu tamanho real no banco de dados. Para saber mais, confira Referência de função SQLSetPos.

SQLMoreResults e SQLDescribeCol

Programas de aplicativo podem chamar SQLDescribeCol para retornar metadados sobre colunas em instruções preparadas. Quando o Always Encrypted estiver habilitado, chamar SQLMoreResultsantes de chamar SQLDescribeCol faz com que sp_describe_first_result_set seja chamado, o que não retorna corretamente os metadados de texto não criptografado de colunas criptografadas. Para evitar esse problema, chame SQLDescribeCol nas instruções preparadas antes de chamar SQLMoreResults.

Controle do impacto sobre o desempenho do Always Encrypted

Como o Always Encrypted é uma tecnologia de criptografia do lado do cliente, a maior parte da sobrecarga de desempenho é observada no lado do cliente, não no banco de dados. Além do custo das operações de criptografia e descriptografia, as outras fontes de sobrecarga de desempenho no lado do cliente são:

  • Viagens de ida e volta adicionais ao banco de dados para recuperar metadados dos parâmetros de consulta.

  • Chamadas para o repositório de chaves mestras de coluna para acessar uma chave mestra de coluna.

Esta seção descreve as otimizações de desempenho internas no ODBC Driver for SQL Server e como você pode controlar o impacto dos dois fatores acima sobre o desempenho.

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

Se o Always Encrypted estiver habilitado para uma conexão, o driver por padrão chamará sys.sp_describe_parameter_encryption para cada consulta parametrizada, passando a instrução de consulta (sem nenhum valor de parâmetro) para o SQL Server. Este procedimento armazenado analisa a instrução de consulta para descobrir se os parâmetros precisam ser criptografados e, se for o caso, retorna as informações relacionadas à criptografia para cada parâmetro para permitir ao driver criptografá-los. O comportamento acima garante um alto nível de transparência ao aplicativo cliente: o aplicativo (e o desenvolvedor do aplicativo) não precisa estar ciente de quais consultas acessam colunas criptografadas, desde que os valores que se destinam às colunas criptografadas sejam passados para o driver nos parâmetros.

A partir da versão 17.6, o driver também armazena em cache os metadados de criptografia para instruções preparadas, o que melhora o desempenho, pois permite que chamadas futuras a SQLExecute não precisem de uma rodada extra de comunicação para recuperar os metadados de criptografia.

Comportamento do Always Encrypted para cada instrução

Para controlar o impacto no desempenho da recuperação de metadados de criptografia para consultas parametrizadas, é possível alterar o comportamento do Always Encrypted para consultas individuais se ele tiver sido habilitado na conexão. Assim, você pode garantir que sys.sp_describe_parameter_encryption seja invocado apenas para consultas que você sabe que têm parâmetros que se destinam a colunas criptografadas. No entanto, observe que, ao fazer isso, você reduzirá a transparência da criptografia: se você criptografar mais colunas no seu banco de dados, poderá precisar alterar o código do aplicativo para alinhá-lo a alterações de esquema.

Para controlar o comportamento de Always Encrypted de uma instrução, chame SQLSetStmtAttr para definir o atributo de instrução SQL_SOPT_SS_COLUMN_ENCRYPTION como um dos seguintes valores:

Valor Descrição
SQL_CE_DISABLED (0) O recurso Always Encrypted está desativado para a instrução
SQL_CE_RESULTSETONLY (1) Apenas descriptografia. Os conjuntos de resultados e valores retornados estão descriptografados, e os parâmetros não estão criptografados
SQL_CE_ENABLED (3) O Always Encrypted está habilitado e é usado para parâmetros e resultados

Novos identificadores de instrução criados a partir de uma conexão com o Always Encrypted habilitado têm SQL_CE_ENABLED como padrão. Handles criados a partir de uma conexão com esse recurso desabilitado assumem SQL_CE_DISABLED por padrão (e não é possível ativar o Always Encrypted nesses handles).

Se a maioria das consultas de um aplicativo cliente acessa colunas criptografadas, é recomendável os seguintes pontos:

  • Defina a palavra-chave de cadeia de conexão ColumnEncryption como Enabled.

  • Defina o atributo SQL_SOPT_SS_COLUMN_ENCRYPTION como SQL_CE_DISABLED em instruções que não acessam nenhuma coluna criptografada. Essa configuração desativará tanto a chamada de sys.sp_describe_parameter_encryption quanto as tentativas de descriptografar quaisquer valores no conjunto de resultados.

  • Defina o atributo SQL_SOPT_SS_COLUMN_ENCRYPTION como SQL_CE_RESULTSETONLY em instruções que não têm parâmetros que exigem criptografia, mas que recuperam dados de colunas criptografadas. Essa configuração desabilita a chamada sys.sp_describe_parameter_encryption e a criptografia do parâmetro. Resultados contendo colunas criptografadas continuam sendo descriptografados.

  • Use as instruções preparadas para consultas que serão executadas mais de uma vez; prepare a consulta com SQLPrepare e salve o identificador de instrução, reutilizando-o com SQLExecute cada vez que for executado. Esse método é a abordagem preferencial para haver bom desempenho mesmo quando não há colunas criptografadas e permite que o driver aproveite os metadados armazenados em cache.

Configurações de segurança do Always Encrypted

Forçar criptografia da coluna

Para impor a criptografia de um parâmetro, defina o campo descritor de parâmetro de implementação (IPD) SQL_CA_SS_FORCE_ENCRYPT por meio de uma chamada para a função SQLSetDescField. Um valor diferente de zero faz com que o driver retorne um erro quando nenhum metadado de criptografia retorna para o parâmetro associado.

SQLHDESC ipd;
SQLGetStmtAttr(hStmt, SQL_ATTR_IMP_PARAM_DESC, &ipd, 0, 0);
SQLSetDescField(ipd, paramNum, SQL_CA_SS_FORCE_ENCRYPT, (SQLPOINTER)TRUE, SQL_IS_SMALLINT);   

Se o SQL Server informar o driver de que o parâmetro não precisa ser criptografado, as consultas que usam tal parâmetro falharão. Esse comportamento oferece proteção extra contra ataques de segurança que envolvem um SQL Server comprometido fornecendo metadados de criptografia incorretos ao cliente, o que pode levar à divulgação de dados.

Armazenamento em cache de chaves de criptografia de coluna

Para reduzir o número de chamadas a um repositório de chaves mestras de coluna para descriptografar chaves de criptografia de coluna, o driver armazena CEKs de texto não criptografado na memória. O cache da CEK é global para o driver e não está associado a nenhuma outra conexão. Depois de receber a ECEK nos metadados do banco de dados, o driver primeiro tenta encontrar no cache a CEK em texto sem formatação correspondente ao valor da chave criptografada. O driver acessa o repositório de chaves que contém a CMK somente se não conseguir localizar a CEK correspondente em texto simples no cache.

Observação

No ODBC Driver for SQL Server, as entradas no cache são removidas após um tempo limite de duas horas. Esse comportamento significa que, para determinada ECEK, o driver entra em contato com o repositório de chaves apenas uma vez durante o tempo de vida do aplicativo, ou a cada duas horas, o que for menor.

Começando com o ODBC Driver 17.1 for SQL Server, o tempo limite de cache da CEK pode ser ajustado usando o atributo de conexão SQL_COPT_SS_CEKCACHETTL, que especifica o número de segundos que uma CEK permanecerá no cache. Devido à natureza do cache global, esse atributo pode ser ajustado de qualquer indicador de conexão válido para o driver. Quando o TTL do cache é reduzido, as CEKs existentes que excederiam o novo TTL também são removidas. Se for zero, nenhuma CEK será armazenada em cache.

Caminhos de chave confiáveis

A partir do ODBC Driver 17.1 for SQL Server, o atributo de conexão SQL_COPT_SS_TRUSTEDCMKPATHS permite que um aplicativo exija que as operações de Always Encrypted usem somente uma lista especificada de CMKs, identificadas por seus caminhos da chave. Por padrão, esse atributo é NULL, o que significa que o driver aceita qualquer caminho de chave. Para usar esse recurso, defina SQL_COPT_SS_TRUSTEDCMKPATHS para apontar para uma sequência de caracteres largos delimitada por nulo e terminada por nulo, que lista o(s) caminho(s) de chave permitido(s). A memória apontada por esse atributo deve permanecer válida durante as operações de criptografia ou descriptografia usando o handle de conexão no qual ele está definido --- momento em que o driver verificará se o caminho da CMK, conforme especificado pelos metadados do servidor, está nessa lista sem diferenciar maiúsculas de minúsculas. Se o caminho da CMK não estiver na lista, a operação falhará. O aplicativo pode alterar o conteúdo da memória para o qual esse atributo aponta, a fim de alterar sua lista de CMKs confiáveis, sem definir o atributo novamente.

Como trabalhar com repositórios de chaves mestras de colunas

Para criptografar ou descriptografar dados, o driver precisa obter uma CEK configurada para a coluna de destino. As CEKs são armazenadas em formato criptografado (ECEKs) nos metadados do banco de dados. Cada CEK tem uma CMK correspondente que foi usada para criptografá-la. Os metadados de banco de dados não armazenam a CMK em si. Eles contêm apenas o nome do repositório de chaves e as informações que esse repositório pode usar para localizar a CMK.

Para obter o valor em texto simples de uma ECEK, o driver primeiro obtém os metadados da CEK e da CMK correspondente e, em seguida, usa essas informações para entrar em contato com o repositório de chaves que contém a CMK e solicitar a descriptografia da ECEK. O driver se comunica com um repositório de chaves usando um provedor de repositório de chaves.

Provedores de repositórios de chaves internos

O ODBC Driver for SQL Server tem os seguintes provedores de repositório de chaves internos:

Nome Descrição Nome do provedor (metadados) Disponibilidade
Azure Key Vault Armazena CMKs em um Azure Key Vault AZURE_KEY_VAULT Windows, macOS, Linux
Repositório de Certificados do Windows Armazena CMKs localmente no repositório de chaves do Windows MSSQL_CERTIFICATE_STORE Windows
  • Você (ou seu DBA) precisa garantir que o nome do provedor, configurado nos metadados da chave mestra de coluna, esteja correto e que o caminho da chave mestra de coluna esteja em conformidade com o formato do caminho da chave para o provedor especificado. É recomendável que você configure as chaves usando ferramentas como SQL Server Management Studio, que gera automaticamente os nomes de provedor válidos e os caminhos de chave ao emitir a CREATE COLUMN MASTER KEY instrução (Transact-SQL).

  • Garanta que seu aplicativo possa acessar a chave no repositório de chaves. Esse processo pode envolver a concessão de acesso para o aplicativo à chave e/ou ao repositório de chaves, dependendo do repositório de chaves, ou a execução de outras etapas de configuração específicas do repositório. Por exemplo, para acessar um Azure Key Vault, você precisa fornecer as credenciais corretas para o repositório de chaves.

Como usar o provedor do Azure Key Vault

O AKV (Azure Key Vault) é uma opção conveniente para armazenar e gerenciar chaves mestras de coluna do Always Encrypted (especialmente se seus aplicativos estiverem hospedados no Azure). O ODBC Driver for SQL Server no Linux, macOS e Windows inclui um provedor de repositório de chaves mestras de coluna interno para o Azure Key Vault. Para obter mais informações sobre como configurar um Azure Key Vault para Always Encrypted, confira Azure Key Vault: passo a passo, Introdução ao Azure Key Vault e Criação de chaves mestras de coluna no Azure Key Vault.

Observação

O driver ODBC oferece suporte apenas à autenticação AKV diretamente no Microsoft Entra ID (anteriormente Azure Active Directory). Caso esteja usando a autenticação do Microsoft Entra para AKV e sua configuração exigir autenticação em um endpoint do Serviços de Federação do Active Directory (AD FS) (AD FS), a autenticação poderá falhar. No Linux e macOS, para a versão de driver 17.2 e posterior, libcurl é necessário para usar esse provedor, mas não é uma dependência explícita, pois outras operações com o driver não precisam dele. Se você encontrar um erro em relação ao libcurl, verifique se ele está instalado.

O driver oferece suporte à autenticação no Azure Key Vault usando os seguintes tipos de credenciais:

  • Nome de usuário/senha: com esse método, as credenciais são o nome de um usuário do Microsoft Entra e sua senha.

  • ID do cliente/segredo: com esse método, as credenciais são uma ID do cliente do aplicativo e um segredo do aplicativo.

  • Identidade gerenciada (17.5.2 +), atribuída pelo sistema ou pelo usuário; para obter mais informações, confira Identidades gerenciadas para recursos do Azure.

  • Azure Key Vault Interativo (drivers do Windows 17.7 ou posteriores) – com esse método, as credenciais são autenticadas por meio do Microsoft Entra ID com ID de logon.

Para permitir que o driver use CMKs armazenadas no AKV para criptografia de colunas, use as seguintes palavras-chave exclusivas da cadeia de conexão:

Tipo de Credencial KeyStoreAuthentication KeyStorePrincipalId KeyStoreSecret
Nome de usuário/senha KeyVaultPassword Nome Principal de Usuário Senha
ID do cliente/segredo KeyVaultClientSecret ID do Cliente Segredo
Identidade Gerenciada KeyVaultManagedIdentity ID do objeto (opcional, somente para IDs atribuídos pelo usuário) (não especificado)
AKV Interativo KeyVaultInteractive (não definido) (não definido)

A partir da versão 17.8, o KeystoreAuthentication e o KeystorePrincipalId podem ser editados usando a interface de configuração de DSN no Administrador de Fonte de Dados ODBC.

Cadeias de conexão de exemplo

As cadeias de conexão a seguir mostram como autenticar no Azure Key Vault com os dois tipos de credenciais:

ClientID/Segredo
"DRIVER=ODBC Driver 18 for SQL Server;SERVER=myServer;Encrypt=yes;Trusted_Connection=Yes;DATABASE=myDB;ColumnEncryption=Enabled;KeyStoreAuthentication=KeyVaultClientSecret;KeyStorePrincipalId=<clientId>;KeyStoreSecret=<secret>"
Nome de usuário/senha
"DRIVER=ODBC Driver 18 for SQL Server;SERVER=myServer;Encrypt=yes;Trusted_Connection=Yes;DATABASE=myDB;ColumnEncryption=Enabled;KeyStoreAuthentication=KeyVaultPassword;KeyStorePrincipalId=<username>;KeyStoreSecret=<password>"
Identidade Gerenciada (atribuída pelo sistema)
"DRIVER=ODBC Driver 18 for SQL Server;SERVER=myServer;Encrypt=yes;Trusted_Connection=Yes;DATABASE=myDB;ColumnEncryption=Enabled;KeyStoreAuthentication=KeyVaultManagedIdentity"
Identidade gerenciada (atribuída pelo usuário)
"DRIVER=ODBC Driver 18 for SQL Server;SERVER=myServer;Encrypt=yes;Trusted_Connection=Yes;DATABASE=myDB;ColumnEncryption=Enabled;KeyStoreAuthentication=KeyVaultManagedIdentity;KeyStorePrincipalId=<objectID>"
AKV Interativo
"DRIVER=ODBC Driver 18 for SQL Server;SERVER=myServer;Encrypt=yes;Trusted_Connection=Yes;DATABASE=myDB;ColumnEncryption=Enabled;KeyStoreAuthentication=KeyVaultInteractive;UID=<userID>;PWD=<password>"

Nenhuma outra alteração de aplicativo de ODBC é necessária para usar o AKV para armazenamento de CMK.

Observação

O driver contém uma lista de endpoints AKV nos quais confia. A partir da versão 17.5.2 do driver, esta lista é configurável: defina a propriedade AKVTrustedEndpoints na chave do Registro ODBCINST.INI ou ODBC.INI do driver ou do DSN (Windows) ou na seção do arquivo odbcinst.ini ou odbc.ini (Linux/macOS) como uma lista delimitada por ponto e vírgula. Configurá-la no DSN tem precedência sobre uma configuração no driver. Se o valor começar com um ponto e vírgula, ele estenderá a lista padrão; caso contrário, substituirá a lista padrão. A lista padrão (da 17.5 em diante) é vault.azure.net;vault.azure.cn;vault.usgovcloudapi.net;vault.microsoftazure.de. Da versão 17.7 em diante, a lista também incluirá o managedhsm.azure.net;managedhsm.azure.cn;managedhsm.usgovcloudapi.net;managedhsm.microsoftazure.de.

Observação

O provedor do Azure Key Vault integrado ao driver ODBC oferece suporte a Cofres e HSMs Gerenciados no Azure Key Vault.

Uso do provedor do Repositório de Certificados do Windows

O ODBC Driver for SQL Server no Windows inclui um provedor de repositório de chaves mestras de coluna interno para o Repositório de Certificados do Windows chamado MSSQL_CERTIFICATE_STORE. (Esse provedor não está disponível no macOS ou no Linux). Com esse provedor, a CMK é armazenada localmente no computador cliente e nenhuma configuração extra pelo aplicativo é necessária para usá-la com o driver. No entanto, o aplicativo deve ter acesso ao certificado e sua chave privada no repositório. Para obter mais informações, consulte Create and Store Column Master Keys (Always Encrypted)Criar e armazenar chaves mestras de coluna (Always Encrypted).

Usar provedores de repositórios de chaves personalizados

O ODBC Driver for SQL Server também oferece suporte a provedores de repositório de chaves personalizado de terceiros usando a interface CEKeystoreProvider. Esse recurso permite que um aplicativo carregue, faça consultas e configure provedores de repositório de chaves para que possam ser usados pelo driver a fim de acessar colunas criptografadas. Os aplicativos podem ainda interagir diretamente com um provedor de repositório de chaves a fim de criptografar as CEKs para armazenamento no SQL Server e executar tarefas além de acessar colunas criptografadas com o ODBC. Para saber mais, confira Provedores do Repositórios de Chaves Personalizados.

Dois atributos de conexão são usados para interagir com provedores de armazenamento de chaves personalizados. Eles são:

  • SQL_COPT_SS_CEKEYSTOREPROVIDER

  • SQL_COPT_SS_CEKEYSTOREDATA

O primeiro é usado para carregar e enumerar os provedores de repositório de chaves carregados, enquanto o segundo permite a comunicação entre provedor e aplicativo. Esses atributos de conexão podem ser usados a qualquer momento, antes ou depois de estabelecer uma conexão, desde que a interação entre provedor e aplicativo não envolva a comunicação com o SQL Server. No entanto, uma vez que o driver não foi carregado ainda, configurar e obter esses atributos antes de conectar fará com que eles sejam processados pelo Gerenciador de Driver, e isso pode não produzir os resultados esperados.

Carregar um provedor de repositório de chaves

Definir o atributo de conexão SQL_COPT_SS_CEKEYSTOREPROVIDER permite que um aplicativo cliente carregue uma biblioteca de provedor, disponibilizando para uso os provedores de repositório de chaves contidos nela.

SQLRETURN SQLSetConnectAttr( SQLHDBC ConnectionHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength);
Argumento Descrição
ConnectionHandle [Entrada] Identificador de conexão. Deve ser um identificador de conexão válido, mas provedores carregados por meio de um identificador de conexão são acessíveis de qualquer outro no mesmo processo.
Attribute [Entrada] Atributo a ser definido: a constante SQL_COPT_SS_CEKEYSTOREPROVIDER.
ValuePtr [Entrada] Ponteiro para uma cadeia de caracteres terminada por caractere nulo que especifica o nome de arquivo da biblioteca do provedor. Para SQLSetConnectAttrA, esse valor é uma cadeia de caracteres ANSI (multibyte). Para SQLSetConnectAttrW, esse valor é uma cadeia de caracteres Unicode (wchar_t).
StringLength [Entrada] O comprimento da string ValuePtr, ou SQL_NTS.

O driver tenta carregar a biblioteca identificada pelo parâmetro ValuePtr usando o mecanismo de carregamento de biblioteca dinâmica definido pela plataforma (dlopen() no Linux e macOS, LoadLibrary() no Windows) e adiciona quaisquer provedores definidos à lista de provedores conhecidos pelo driver. Podem ocorrer os seguintes erros:

Erro Descrição
CE203 Não foi possível carregar a biblioteca dinâmica.
CE203 O símbolo exportado de "CEKeyStoreProvider" não foi encontrado na biblioteca.
CE203 Um ou mais provedores da biblioteca já estão carregados.

SQLSetConnectAttr retorna o erro ou valores de êxito comuns, e informações adicionais estão disponíveis para erros que ocorreram por meio do mecanismo de diagnóstico padrão de ODBC.

Observação

O programador do aplicativo deve garantir que quaisquer provedores personalizados sejam carregados antes de qualquer consulta que exija o envio deles por uma conexão. Não fazer isso resultará no erro:

Erro Descrição
CE200 Provedor do repositório de chaves %1 não encontrado. Verifique se a biblioteca do provedor do repositório de chaves adequada foi carregada.

Observação

Os implementadores do provedor do repositório de chaves devem evitar usar MSSQL no nome de seus provedores personalizados. Esse termo é reservado exclusivamente para uso da Microsoft e pode causar conflitos com futuros provedores internos. Usar esse termo no nome de um provedor personalizado pode gerar um aviso do ODBC.

Obter a lista de provedores carregados

Obter este atributo de conexão permite que um aplicativo cliente determine os provedores de repositório de chaves atualmente carregados no driver (incluindo os internos). Esse processo só pode ser executado após a conexão.

SQLRETURN SQLGetConnectAttr( SQLHDBC ConnectionHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, SQLINTEGER * StringLengthPtr);
Argumento Descrição
ConnectionHandle [Entrada] Identificador de conexão. Deve ser um identificador de conexão válido, mas provedores carregados por meio de um identificador de conexão são acessíveis de qualquer outro no mesmo processo.
Attribute [Entrada] Atributo a ser recuperado: a constante SQL_COPT_SS_CEKEYSTOREPROVIDER.
ValuePtr [Saída] Um ponteiro para a memória onde será retornado o nome do próximo provedor carregado.
BufferLength [Entrada] O comprimento do buffer ValuePtr.
StringLengthPtr [Saída] Um ponteiro para um buffer no qual será retornado o número total de bytes (excluindo o caractere de terminação nula) disponíveis para retorno em *ValuePtr. Se ValuePtr for um ponteiro nulo, nenhum comprimento retorna. Se o valor do atributo é uma cadeia de caracteres e o número de bytes disponível para retornar é maior que BufferLength menos o comprimento do caractere de terminação nula, os dados em *ValuePtr são truncados para BufferLength menos o comprimento do caractere de terminação nula e os dados são terminados em nulo pelo driver.

Para permitir recuperar a lista inteira, todas as operações Get retornam o nome do provedor atual e incrementam um contador interno para o próximo. Quando esse contador atinge o final da lista, uma cadeia de caracteres vazia ("") é retornada, e o contador é redefinido; em seguida, operações Get subsequentes continuam novamente a partir do início da lista.

Comunicar-se com os provedores de repositórios de chaves

O atributo de conexão SQL_COPT_SS_CEKEYSTOREDATA permite que um aplicativo cliente se comunique com provedores de repositórios de chaves carregados para configurar outros parâmetros, material de criação de chaves etc. A comunicação entre um aplicativo cliente e um provedor segue um protocolo simples de solicitação e resposta, com base em solicitações Get e Set, utilizando esse atributo de conexão. A comunicação é iniciada somente pelo aplicativo cliente.

Observação

Devido à natureza das chamadas ODBC que o CEKeyStoreProvider atende (SQLGet/SetConnectAttr), a interface ODBC suporta apenas a definição de dados no nível do contexto da conexão.

O aplicativo se comunica com provedores de armazenamento de chaves por meio do driver, usando a estrutura CEKeystoreData:

typedef struct CEKeystoreData {
wchar_t *name;
unsigned int dataSize;
char data[];
} CEKEYSTOREDATA;
Argumento Descrição
name [Entrada] Ao definir, o nome do provedor ao qual os dados são enviados. Ignorado após Get. Sequência de caracteres largos terminada por nulo.
dataSize [Entrada] O tamanho da matriz de dados seguindo a estrutura.
data [InOut] Ao definir, os dados a serem enviados ao provedor. Esses dados podem ser dados arbitrários. O driver não faz nenhuma tentativa de interpretá-los. Após Get, o buffer para receber os dados lidos do provedor.

Gravando dados em um provedor

Uma chamada SQLSetConnectAttr usando o atributo SQL_COPT_SS_CEKEYSTOREDATA grava um "pacote" de dados em um provedor de repositório de chaves especificado.

SQLRETURN SQLSetConnectAttr( SQLHDBC ConnectionHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength);
Argumento Descrição
ConnectionHandle [Entrada] Identificador de conexão. Deve ser um identificador de conexão válido, mas provedores carregados por meio de um identificador de conexão são acessíveis de qualquer outro no mesmo processo.
Attribute [Entrada] Atributo a ser definido: a constante SQL_COPT_SS_CEKEYSTOREDATA.
ValuePtr [Entrada] Ponteiro para uma estrutura CEKeystoreData. O campo de nome da estrutura identifica o provedor para o qual os dados são destinados.
StringLength [Entrada] Constante SQL_IS_POINTER

É possível obter informações de erro mais detalhadas com SQLGetDiacRec.

Observação

O provedor pode usar o identificador de conexão para associar os dados gravados a uma conexão específica, se desejado. Esse recurso é útil para implementar a configuração por conexão. Ele também pode ignorar o contexto de conexão e tratar os dados de forma idêntica, independentemente da conexão usada para enviar os dados. Para obter mais informações, confira Associação de contexto.

Ler dados de um provedor

Uma chamada para SQLGetConnectAttr usando o atributo SQL_COPT_SS_CEKEYSTOREDATA lê um "pacote" de dados do provedor no qual houve a gravação mais recente. Se não houver nenhum, ocorre um erro de sequência de função. É recomendável que os implementadores do provedor de armazenamento de chaves suportem "gravações fictícias" de 0 bytes como forma de selecionar o provedor para operações de leitura sem causar outros efeitos secundários, se isso fizer sentido.

SQLRETURN SQLGetConnectAttr( SQLHDBC ConnectionHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, SQLINTEGER * StringLengthPtr);
Argumento Descrição
ConnectionHandle [Entrada] Identificador de conexão. Deve ser um identificador de conexão válido, mas provedores carregados por meio de um identificador de conexão são acessíveis de qualquer outro no mesmo processo.
Attribute [Entrada] Atributo a ser recuperado: a constante SQL_COPT_SS_CEKEYSTOREDATA.
ValuePtr [Saída] Um ponteiro para uma estrutura do tipo CEKeystoreData na qual são colocados os dados lidos do provedor.
BufferLength [Entrada] Constante SQL_IS_POINTER
StringLengthPtr [Saída] Um ponteiro para um buffer no qual BufferLength será retornado. Se *ValuePtr for um ponteiro nulo, nenhum comprimento será retornado.

O chamador deve garantir que seja alocado um buffer com comprimento suficiente após a estrutura CEKEYSTOREDATA para que o provedor grave dados nele. Ao retornar, o campo dataSize é atualizado com o tamanho real dos dados lidos do provedor. É possível obter informações de erro mais detalhadas com SQLGetDiacRec.

Essa interface não exige requisitos adicionais sobre o formato dos dados transferidos entre um aplicativo e um provedor de repositório de chaves. Cada provedor pode definir seu próprio formato de dados/protocolo, dependendo das suas necessidades.

Para obter um exemplo de implementação de seu próprio provedor de repositório de chaves, confira Provedores de repositório de chaves personalizados

Limitações do driver ODBC ao usar o Always Encrypted

Operações assíncronas

Embora o driver ODBC permita o uso de operações assíncronas com o Always Encrypted, há um impacto no desempenho das operações quando o Always Encrypted está habilitado. A chamada para sys.sp_describe_parameter_encryption para determinar os metadados de criptografia do comando é bloqueante e fará com que o driver aguarde que o servidor retorne os metadados antes de devolver SQL_STILL_EXECUTING.

Recuperar dados em partes com SQLGetData

Não é possível recuperar caracteres criptografados e colunas binárias em partes com o SQLGetData, antes de implantar o ODBC Driver 17 for SQL Server. É possível fazer apenas uma chamada para o SQLGetData com um buffer de comprimento adequado para incluir os dados da coluna inteira.

Enviar dados em partes com SQLPutData

Antes do ODBC Driver 17.3 for SQL Server, não é possível enviar dados de inserção ou comparação em partes com o SQLPutData. É possível fazer apenas uma chamada para o SQLPutData com um buffer contendo os dados completos. Para inserir dados longos em colunas criptografadas, use a API de Cópia em Massa, com um arquivo de dados de entrada, conforme descrito na seção a seguir.

Money e smallmoney criptografados

Não é possível direcionar as colunas money ou smallmoney criptografadas por parâmetros, porque não há tipos de dados ODBC específicos que mapeiem para esses tipos, gerando erros de Conflito de Tipo Operando.

Cópia em massa de colunas criptografadas

O uso das funções de Cópia em Massa do SQL e do utilitário bcp tem suporte com o Always Encrypted, após a implantação do ODBC Driver 17 for SQL Server. Tanto o texto simples (criptografado na inserção e descriptografado durante a recuperação) quanto o texto cifrado (transferido literalmente) podem ser inseridos e recuperados usando as APIs de cópia em massa (bcp_*) e o utilitário bcp.

  • Para recuperar texto cifrado no formulário varbinary(max) (por exemplo, para carregamento em massa em um banco de dados diferente), conecte-se sem a opção ColumnEncryption (ou defina-a como Disabled) e execute uma operação BCP OUT.

  • Para inserir e recuperar texto não criptografado e permitir que o driver execute criptografia e descriptografia de modo transparente conforme necessário, basta definir ColumnEncryption como Enabled. Caso contrário, a funcionalidade da API BCP permanece inalterada.

  • Para inserir texto cifrado no formulário varbinary(max) (por exemplo, como recuperado acima), defina a opção BCPMODIFYENCRYPTED como TRUE e execute uma operação BCP IN. Para que os dados resultantes possam ser descriptografados, verifique se a CEK da coluna de destino é a mesma CEK daquela da qual o texto cifrado foi originalmente obtido.

Ao usar o utilitário bcp: para controlar a configuração ColumnEncryption, use a opção -D e especifique um DSN que contenha o valor desejado. Para inserir texto cifrado, verifique se a configuração ALLOW_ENCRYPTED_VALUE_MODIFICATIONS do usuário está habilitada.

A tabela a seguir fornece um resumo das ações ao operar em uma coluna criptografada:

ColumnEncryption Sentido do BCP Descrição
Disabled SAÍDA (para o cliente) Recupera o texto cifrado. O tipo de dados observado é varbinary(max) .
Enabled OUT (para o cliente) Recupera texto não criptografado. O driver descriptografará os dados da coluna.
Disabled IN (para o servidor) Insere o texto cifrado. Essa configuração destina-se a mover os dados criptografados discretamente sem a necessidade de serem descriptografados. A operação falhará se a opção ALLOW_ENCRYPTED_VALUE_MODIFICATIONS não estiver definida no usuário ou se BCPMODIFYENCRYPTED não estiver definida no identificador de conexão. Para obter mais informações, veja abaixo.
Enabled IN (para o servidor) Insere texto não criptografado. O driver criptografará os dados da coluna.

A opção BCPMODIFYENCRYPTED

Para evitar dados corrompidos, o servidor normalmente não permite inserir texto cifrado diretamente em uma coluna criptografada e, portanto, tentar fazer isso gera uma falha. No entanto, para carregamento em massa de dados criptografados usando a API BCP, configurar a opção BCPMODIFYENCRYPTEDbcp_control como TRUE permite que o texto cifrado seja inserido diretamente e reduz o risco de corromper os dados criptografados pela configuração da opção ALLOW_ENCRYPTED_VALUE_MODIFICATIONS na conta de usuário. No entanto, as chaves devem corresponder aos dados, e recomenda-se realizar algumas verificações de somente leitura dos dados inseridos após a inserção em massa e antes de um uso futuro.

Para obter mais informações, veja Migrar dados confidenciais protegidos por Always Encrypted.

Resumo da API do Always Encrypted

Palavras-chave de cadeia de conexão

Nome Descrição
ColumnEncryption Os valores aceitos são Enabled/Disabled.
Enabled – habilita a funcionalidade Always Encrypted para a conexão.
Disabled – desabilita a funcionalidade Always Encrypted para a conexão.
protocolo de atestado, URL de atestado – (versão 17.4 e posterior) habilita o Always Encrypted com enclave seguro usando o protocolo de atestado especificado e a URL de atestado.

O padrão é Disabled.
KeyStoreAuthentication Valores válidos: KeyVaultPassword, KeyVaultClientSecret, KeyVaultInteractive e KeyVaultManagedIdentity
KeyStorePrincipalId Quando KeyStoreAuthentication = KeyVaultPassword, defina este valor como um nome principal do usuário válido do Microsoft Entra.
Quando KeyStoreAuthentication = KeyVaultClientSecret definido esse valor para um ID de cliente de aplicativo Microsoft Entra válido
Quando KeyStoreAuthentication = KeyVaultManagedIdentity, defina esse valor como o ID do objeto de uma identidade gerenciada atribuída pelo usuário. Se nenhum valor for fornecido, a identidade gerenciada atribuída pelo sistema será usada.
KeyStoreSecret Quando KeyStoreAuthentication = KeyVaultPassword, defina este valor como a senha para o nome de usuário correspondente.
Quando KeyStoreAuthentication = KeyVaultClientSecret defina este valor como o segredo da aplicação associado a uma ID de cliente de aplicação Microsoft Entra válida

Atributos de conexão

Nome Tipo Descrição
SQL_COPT_SS_COLUMN_ENCRYPTION Pré-conexão SQL_COLUMN_ENCRYPTION_DISABLE (0) – desabilitar o Always Encrypted
SQL_COLUMN_ENCRYPTION_ENABLE (1) – habilitar o Always Encrypted
ponteiro para a cadeia de caracteres *attestation protocol*,*attestation URL* – (versão 17.4 e posterior) habilitar com o enclave seguro
SQL_COPT_SS_CEKEYSTOREPROVIDER Pós-conexão [Set] – Tentativa de carregar CEKeystoreProvider
[Get] - Retorna o nome do CEKeystoreProvider
SQL_COPT_SS_CEKEYSTOREDATA Pós-conexão [Set] – Gravar dados em CEKeystoreProvider
[Get] - Ler dados de CEKeystoreProvider
SQL_COPT_SS_CEKCACHETTL Pós-conexão [Set] – Definir o TTL do cache da CEK
[Get] - Obter o TTL atual do cache da CEK
SQL_COPT_SS_TRUSTEDCMKPATHS Pós-conexão [Set] - Definir o ponteiro para os caminhos confiáveis da CMK
[Get] - Obter o ponteiro dos caminhos confiáveis de CMK atuais

Atributos da declaração

Nome Descrição
SQL_SOPT_SS_COLUMN_ENCRYPTION SQL_CE_DISABLED (0) - O Always Encrypted está desabilitado para a instrução
SQL_CE_RESULTSETONLY (1) – apenas descriptografia. Os conjuntos de resultados e valores retornados estão descriptografados, e os parâmetros não estão criptografados
SQL_CE_ENABLED (3) – o Always Encrypted está habilitado e é usado para parâmetros e resultados

Campos de descritor

Campo IPD Tamanho/tipo Valor Padrão Descrição
SQL_CA_SS_FORCE_ENCRYPT (1236) WORD (2 bytes) 0 Quando 0 (padrão): a decisão de criptografar esse parâmetro é determinada pela disponibilidade de metadados de criptografia.

Quando for diferente de zero: se os metadados de criptografia estão disponíveis para esse parâmetro, eles são criptografados. Caso contrário, a solicitação falha com o erro [CE300] [Microsoft][ODBC Driver 17 for SQL Server]A criptografia obrigatória foi especificada para um parâmetro, mas nenhum metadado de criptografia foi fornecido pelo servidor.

Opções de bcp_control

Nome da opção Valor Padrão Descrição
BCPMODIFYENCRYPTED (21) FALSE Quando TRUE, permite que valores varbinary(max) sejam inseridos em uma coluna criptografada. Quando FALSE, evita a inserção, a menos que o tipo e os metadados de criptografia corretos sejam fornecidos.

Solução de problemas

Ao encontrar dificuldades no uso do Always Encrypted, comece verificando os seguintes pontos:

  • A CEK que criptografa a coluna desejada está presente e acessível no servidor.

  • A CMK que criptografa a CEK tem metadados acessíveis no servidor e também pode ser acessada do cliente.

  • ColumnEncryption está habilitado no DSN, na cadeia de conexão ou no atributo de conexão e, se usa o enclave seguro, tem o formato correto.

Além disso, ao usar o enclave seguro, as falhas de atestado identificam a etapa no processo de atestado em que a falha ocorreu, de acordo com a seguinte tabela:

Etapa Descrição
0 a 99 Resposta de atestado inválida ou erro de verificação de assinatura.
100 a 199 Erro ao recuperar certificados da URL de atestação. Verifique se <attestation URL>/v2.0/signingCertificates é válido e está acessível.
200 a 299 Formato inesperado ou incorreto da identidade do enclave.
300 a 399 Erro ao estabelecer o canal seguro com o enclave.

Confira também