Использование функции Always Encrypted с драйвером ODBC для SQL Server

Скачать драйвер ODBC

Применимо к

  • ODBC Driver 13.1+ for SQL Server

Введение

В этой статье содержатся сведения о разработке приложений ODBC с помощью Always Encrypted (ядро СУБД) или Always Encrypted с безопасными анклавами и ODBC Driver for SQL Server.

Функция Always Encrypted позволяет шифровать конфиденциальные данные в клиентских приложениях, не раскрывая данные или ключи шифрования для SQL Server или Базы данных SQL Azure. Драйвер с поддержкой Always Encrypted, такой как драйвер ODBC для SQL Server, реализует эту функцию безопасности за счет прозрачного шифрования и расшифровки конфиденциальных данных в клиентском приложении. Драйвер автоматически определяет, какие параметры запроса соответствуют важным столбцам базы данных (защищенным с помощью Always Encrypted), и шифрует значения этих параметров перед передачей данных в SQL Server или Базу данных SQL Azure. Аналогичным образом драйвер прозрачно расшифровывает данные, полученные из зашифрованных столбцов базы в результатах запроса. Always Encrypted с безопасными анклавами обеспечивает расширенные функции защиты конфиденциальных данных.

Дополнительные сведения см. в статьях об использовании Always Encrypted (ядро СУБД) и Always Encrypted с безопасными анклавами.

Необходимые компоненты

Настройте функцию постоянного шифрования в базе данных. В процесс настройки входят действия по подготовке ключей Always Encrypted и настройке шифрования для выбранных столбцов базы данных. Если у вас еще нет базы данных с настроенным Always Encrypted, следуйте инструкциям в руководстве по началу работы с Always Encrypted. В частности база данных должна содержать определения метаданных для главного ключа столбца (CMK), ключа шифрования столбца (CEK) и таблицы с одним или несколькими столбцами, зашифрованными с помощью этого ключа CEK.

Если вы используете Always Encrypted с безопасными анклавами, см. статью Разработка приложений с помощью Always Encrypted с безопасными анклавами со сведениями о дополнительных предварительных требованиях.

Включение Always Encrypted в приложении ODBC

Самым простым способом одновременно включить шифрование параметров и расшифровку столбца с зашифрованным набором результатов будет установка значения Enabled для ключевого слова строки подключения ColumnEncryption. Ниже приведен пример кода со строкой подключения, которая позволяет включить Always Encrypted.

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

Always Encrypted также можно включить в конфигурации имени DSN, используя тот же ключ и значение (которое переопределяется параметром строки подключения, если он указан), или указать программно с помощью атрибута предварительного подключения SQL_COPT_SS_COLUMN_ENCRYPTION. Такая настройка переопределяет значение, указанное в строке подключения или имени DSN:

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

Включив Always Encrypted для подключения, вы можете изменять его поведение для отдельных запросов. Дополнительные сведения см. в разделе Управление влиянием Always Encrypted на производительность ниже.

Включенной функции Always Encrypted недостаточно для успешного шифрования или расшифровки. Вам нужно убедиться в следующем:

  • Приложение имеет разрешения VIEW ANY COLUMN MASTER KEY DEFINITION и VIEW ANY COLUMN ENCRYPTION KEY DEFINITION для базы данных, необходимые для доступа к метаданным о ключах постоянного шифрования в базе данных. Дополнительные сведения см. в описании разрешений базы данных.

  • Приложение может обращаться к CMK для защиты ключей CEK для запрашиваемых зашифрованных столбцов. Это поведение зависит от поставщика хранилища ключей, который хранит CMK. См. подробнее о работе с хранилищами главных ключей столбцов.

Включение Always Encrypted с безопасными анклавами

Примечание.

В Linux и macOS для использования Always Encrypted с безопасными анклавами требуется OpenSSL версии 1.0.1 или более поздней.

Начиная с версии 17.4 драйвер поддерживает Always Encrypted с безопасными анклавами. Чтобы включить использование анклава при подключении к базе данных, задайте ключ DSN ColumnEncryption, ключевое слово строки подключения или атрибут подключения, указав значение <attestation protocol>,<attestation URL>, где:

  • <attestation protocol> — определяет протокол, используемый для аттестации анклава.

    • Если вы используете SQL Server и службу защиты узлов (HGS), <attestation protocol> необходимо VBS-HGS.
    • Если вы используете База данных SQL Azure и Microsoft Аттестация Azure, <attestation protocol> должны бытьSGX-AAS.
    • Если аттестация не требуется, параметр <attestation-protocol> должен иметь значение VBS-NONE. (Версия 18.1 и выше)
  • <attestation URL> — определяет URL-адрес аттестации (конечная точка службы аттестации). Необходимо получить URL-адрес аттестации для имеющейся среды у администратора службы аттестации.

Примеры строк подключения, позволяющие включить вычисления анклавов для подключений к базе данных:

  • 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"
    
  • База данных SQL Azure:

    "Driver=ODBC Driver 18 for SQL Server;Server=myServer.database.windows.net;Database=myDataBase;Uid=myUsername;Pwd=myPassword;Encrypt=yes;ColumnEncryption=SGX-AAS,https://myAttestationProvider.uks.attest.azure.net/"
    
  • Без аттестации (версия 18.1 и выше):

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

Если сервер и служба аттестации, а также ключи CMK и CEK с поддержкой анклава настроены правильно для зашифрованных столбцов, вы сможете выполнять запросы, использующие анклав, например шифрование на месте и расширенные вычисления, в дополнение к существующим функциональным возможностям, которые предоставляет Always Encrypted. Дополнительные сведения см. в статье Настройка и использование Always Encrypted с безопасными анклавами.

Получение и изменение данных в зашифрованных столбцах

После включения Always Encrypted для подключения можно использовать стандартные API-интерфейсы ODBC. API-интерфейсы ODBC могут извлекать или изменять данные в зашифрованных столбцах базы данных. Вам могут быть полезны следующие ресурсы документации:

Приложение должно иметь необходимые разрешения базы данных и доступ к главному ключу столбца. Затем драйвер шифрует все параметры запроса, предназначенные для зашифрованных столбцов. Драйвер также расшифровывает данные, полученные из зашифрованных столбцов. Драйвер выполняет все операции шифрования и расшифровки без участия исходного кода. Для программы все выглядит так, как будто столбцы не зашифрованы.

Если функция Always Encrypted не включена, выполнение запросов с параметрами, предназначенными для зашифрованных столбцов, завершится ошибкой. Данные по-прежнему могут извлекаться из зашифрованных столбцов, пока для них не будут указаны параметры, предназначенные для зашифрованных столбцов. Однако драйвер не будет применять расшифровку и приложение получит двоичные зашифрованные данные (в виде массивов байтов).

В приведенной ниже таблице описывается поведение запросов в зависимости от того, включена функция Always Encrypted или нет.

Характеристика запроса Постоянное шифрование включено, и приложение может получать доступ к ключам и метаданным ключей Функция Always Encrypted включена, и приложение не может получать доступ к ключам и метаданным ключей Постоянное шифрование отключено
Параметры, предназначенные для зашифрованных столбцов. Значения параметров прозрачно шифруются. Ошибка Ошибка
Извлечение данных из зашифрованных столбцов без параметров, предназначенных для зашифрованных столбцов. Результаты из зашифрованных столбцов прозрачно расшифровываются. Приложение получает значения столбца в виде обычного текста. Ошибка Результаты из зашифрованных столбцов не расшифровываются. Приложение получает зашифрованные значения в виде массивов байтов.

В следующих примерах показано получение и изменение данных в зашифрованных столбцах. В этом примере предполагается, что таблица имеет следующую схему. Столбцы SSN и BirthDate зашифрованы.

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

Пример вставки данных

В этом примере показана вставка строки в таблицу Patients. Заметьте следующие детали:

  • В примере кода нет ничего, связанного с шифрованием. Драйвер автоматически обнаруживает и шифрует значения параметров SSN и даты, которые предназначены зашифрованных столбцов. Благодаря этому шифрование является прозрачным для приложения.

  • Данные, вставленные в столбцы базы данных (в том числе в зашифрованные) передаются в качестве привязанных параметров (см. Функция SQLBindParameter). Несмотря на то, что при отправке значений в незашифрованные столбцы использовать параметры необязательно (но настоятельно рекомендуется, так как это помогает предотвратить внедрение кода SQL), они требуются для значений, предназначенных для зашифрованных столбцов. Если значения, вставленные в столбцы SSN или BirthDate, были переданы в качестве внедренных в инструкцию запроса литералов, выполнение запроса завершится ошибкой, так как драйвер не пытается шифровать или иным образом обрабатывать литералы в запросах. В результате сервер отклонит их как несовместимые с зашифрованными столбцами.

  • Для параметров, вставляемых в столбец SSN, устанавливается тип SQL SQL_CHAR, который сопоставляется с типом данных SQL Server char (rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 11, 0, (SQLPOINTER)SSN, 0, &cbSSN);). Если для параметра был задан тип SQL_WCHAR, который сопоставляется с типом данных nchar, выполнение запроса завершится ошибкой, так как Always Encrypted не поддерживает преобразования на стороне сервера из зашифрованных значений nchar в зашифрованные значения char. Сведения о сопоставлении типов данных см. в этом приложении справочника по программированию ODBC о типах данных.

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

Пример получения данных в виде открытого текста

В следующем примере показана фильтрация данных на основе зашифрованных значений и получение данных в виде открытого текста из зашифрованных столбцов. Заметьте следующие детали:

  • Значение, используемое в предложении WHERE для фильтрации по столбцу SSN, необходимо передавать, используя SQLBindParameter, чтобы перед отправкой на сервер драйвер мог его прозрачно зашифровать.

  • Все значения, выводимые программой, будут представлены в виде обычного текста, так как драйвер прозрачно расшифрует данные, полученные из столбцов SSN и BirthDate.

Примечание.

Запросы могут выполнять сравнения равенства по зашифрованным столбцам только в том случае, если шифрование детерминировано или включено безопасное анклав.

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

Пример получения данных в виде зашифрованного текста

Если функция Always Encrypted не включена, запрос может получать данные из зашифрованных столбцов, пока для него не будут указаны параметры, предназначенные для зашифрованных столбцов.

В приведенном ниже примере показано извлечение двоичных зашифрованных данных из зашифрованных столбцов. Заметьте следующие детали:

  • Так как Always Encrypted не включается в строке подключения, запрос вернет зашифрованные значения SSN и BirthDate в виде байтовых массивов (программа преобразует эти значения в строки).
  • Запрос, получающий данные из зашифрованных столбцов с отключенным постоянным шифрованием, может иметь параметры при условии, что ни один из параметров не предназначен для зашифрованного столбца. Приведенный выше запрос выполняет фильтрацию по LastName, который не зашифрован в базе данных. Запрос, отфильтрованный по SSN или BirthDate, завершится ошибкой.
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);        
}

Шифрование Money и SmallMoney

Начиная с версии драйвера 17.7, функцию Always Encrypted можно использовать с типами MONEY и SMALLMONEY. Однако необходимо выполнить ряд дополнительных действий. При вставке данных в зашифрованные столбцы MONEY или SMALLMONEY используйте один из следующих типов 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

и тип SQL SQL_NUMERIC или SQL_DOUBLE (при использовании этого типа точность может снижаться).

Привязка переменной

При привязке переменной типа MONEY или SMALLMONEY в зашифрованном столбце необходимо задать следующие поля дескриптора:

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

Как избежать распространенных проблем при запросе зашифрованных столбцов

В этом разделе описываются общие категории ошибок, возникающих при выполнении запросов к зашифрованным столбцам из приложений ODBC, и приводятся рекомендации о том, как их избежать.

Ошибки преобразования неподдерживаемых типов данных

Постоянное шифрование поддерживает несколько преобразований для зашифрованных типов данных. Подробный список поддерживаемых преобразований типов см. в статье Always Encrypted (ядро СУБД). Чтобы избежать ошибок преобразования типов данных, старайтесь соблюдать следующие моменты при использовании SQLBindParameter с параметрами, предназначенными для зашифрованных столбцов.

  • Тип SQL для параметра всегда точно совпадает с типом целевого столбца или может быть преобразован в тип этого столбца.

  • Точность и масштаб параметров, предназначенных для столбцов типов данных SQL Server decimal и numeric, соответствуют точности и масштабу, настроенным для конечного столбца.

  • В запросах, которые изменяют конечный столбец, точность параметров, предназначенных для столбцов типов данных SQL Server datetime2, datetimeoffset или time, не превышает точность для конечного столбца.

Ошибки, возникающие из-за передачи значений в виде открытого текста, а не в зашифрованном виде

Любое значение, предназначенное для зашифрованного столбца, должно быть зашифровано до отправки на сервер. Попытка вставки, изменения или фильтрации по значению в виде открытого текста в зашифрованном столбце приведет к возникновению ошибки. Чтобы избежать таких ошибок, убедитесь в том, что:

  • Функция Always Encrypted включена (через имя DSN, в строке подключения, перед подключением через атрибут подключения SQL_COPT_SS_COLUMN_ENCRYPTION для конкретного подключения или через атрибут инструкции SQL_SOPT_SS_COLUMN_ENCRYPTION для конкретной инструкции).

  • для отправки данных, предназначенных для зашифрованных столбцов, используется параметр SQLBindParameter. В примере ниже показан запрос, который вместо передачи литерала как аргумента объекта SQLBindParameter неправильно выполняет фильтрацию по литералу или константе в зашифрованном столбце (SSN).

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

Меры предосторожности при использовании SQLSetPos и SQLMoreResults

функция SQLSetPos;

API SQLSetPos позволяет приложению обновлять строки в результирующем наборе с использованием буферов, которые были привязаны с помощью SQLBindCol и в которые были получены данные строк. Из-за асимметричного поведения заполнения для зашифрованных типов фиксированной длины существует риск неожиданно изменить данные в этих столбцах при обновлении других столбцов в той же строке. При использовании Always Encrypted символьные значения фиксированной длины будут заполняться в тех случаях, когда значение меньше размера буфера.

Чтобы устранить это поведение, установите флаг SQL_COLUMN_IGNORE для пропуска тех столбцов, которые не должны обновляться при операции SQLBulkOperations или использовании SQLSetPos для обновлений на основе курсора. Все столбцы, которые не изменяются приложением напрямую, следует игнорировать по соображениям производительности и для того, чтобы избежать усечения столбцов, привязанных к буферу меньше их фактического размера (в базе данных). Дополнительные сведения см. в справочнике по функции SQLSetPos.

SQLMoreResults и SQLDescribeCol

Приложения могут вызывать SQLDescribeCol, чтобы возвращать метаданные о столбцах в подготовленных инструкциях. Если включена функция Always Encrypted, вызов SQLMoreResultsперед вызовом SQLDescribeCol приводит к вызову sp_describe_first_result_set, в результате чего неверно возвращаются метаданные в формате открытого текста для зашифрованных столбцов. Чтобы избежать этой проблемы, вызывайте SQLDescribeCol для подготовленных инструкций перед вызовом SQLMoreResults.

Управление влиянием Always Encrypted на производительность

Так как Always Encrypted является технологией шифрования на стороне клиента, значительное влияние на производительность происходит на стороне клиента, а не в базе данных. Помимо затрат на операции шифрования и расшифровки существуют и другие источники снижения производительности на стороне клиента:

  • Дополнительные обращения к базе данных для получения метаданных для параметров запроса.

  • Вызовы хранилища главных ключей столбцов для доступа к главному ключу столбца.

В этом разделе описываются процессы оптимизации производительности, встроенные в ODBC Driver for SQL Server, и способы управления влиянием двух указанных выше факторов на производительность.

Управление круговыми путями для получения метаданных параметров запроса

Если для соединения включена функция Always Encrypted, по умолчанию драйвер будет вызывать sys.sp_describe_parameter_encryption для каждого параметризованного запроса, передавая инструкцию запроса (без значений параметров) в SQL Server. Эта хранимая процедура анализирует инструкцию запроса и выясняет, нужно ли шифровать параметры. Если да, то она возвращает по каждому параметру связанные с шифрованием сведения, позволяющие драйверу шифровать их. Описанное выше поведение гарантирует высокий уровень прозрачности клиентского приложения. Приложению (и разработчику) не нужно знать, какие запросы обращаются к зашифрованным столбцам, если все значения для этих зашифрованных столбцов передаются драйверу в параметрах.

Начиная с версии 17.6, драйвер также кэширует метаданные шифрования для подготовленных инструкций, повышая производительность и позволяя следующим вызовам к SQLExecute обходиться без дополнительного кругового пути для получения метаданных шифрования.

Поведение Always Encrypted для отдельных инструкций

Чтобы управлять тем, как получение метаданных шифрования для параметризованных запросов влияет на производительность, вы можете изменять поведение Always Encrypted для отдельных запросов, если эта функция уже включена для подключения. Это позволяет гарантировать, что sys.sp_describe_parameter_encryption вызывается только для тех запросов, в которых точно есть параметры для зашифрованных столбцов. Обратите внимание, что в этом случае снижается прозрачность шифрования: при шифровании дополнительных столбцов в базе данных может потребоваться изменить код приложения в соответствии с изменениями схемы.

Чтобы изменить поведение Always Encrypted для инструкции, вызовите SQLSetStmtAttr и задайте одно из следующих значений для атрибута инструкции SQL_SOPT_SS_COLUMN_ENCRYPTION:

значение Описание
SQL_CE_DISABLED (0) Функция Always Encrypted отключена для инструкции
SQL_CE_RESULTSETONLY (1); Только расшифровка. Расшифровываются результирующие наборы и возвращаемые значения, а параметры не шифруются.
SQL_CE_ENABLED (3); Функция Always Encrypted включена и используется как для параметров, так и для результатов

Новые дескрипторы инструкций, которые создаются для подключения с включенной функцией Always Encrypted, по умолчанию получают значение SQL_CE_ENABLED. Дескрипторы, создаваемые для подключений, где эта функция отключена, по умолчанию получают значение SQL_CE_DISABLED (и вы не сможете включить для них Always Encrypted).

Если большинство запросов клиентского приложения обращаются к зашифрованным столбцам, мы рекомендуем выполнить следующее.

  • Присвойте ключевому слову ColumnEncryption строки подключения значение Enabled.

  • Задайте атрибуту SQL_SOPT_SS_COLUMN_ENCRYPTION значение SQL_CE_DISABLED для инструкций, которые не обращаются к зашифрованным столбцам. Этот параметр отключает возможность вызова sys.sp_describe_parameter_encryption и расшифровки значений в результирующем наборе.

  • Задайте атрибуту SQL_SOPT_SS_COLUMN_ENCRYPTION значение SQL_CE_RESULTSETONLY для инструкций, которые не имеют требующих шифрования параметров, но получают данные из зашифрованных столбцов. Этот параметр отключает вызов sys.sp_describe_parameter_encryption и шифрование параметров. Результаты, содержащие зашифрованные столбцы, будут по-прежнему расшифровываться.

  • Используйте подготовленные инструкции для запросов, которые будут выполняться многократно; подготовьте запрос с помощью SQLPrepare и сохраните дескриптор инструкции, указывая его в SQLExecute при каждом очередном выполнении. Этот метод является рекомендуемым для поддержания производительности даже при отсутствии зашифрованных столбцов и позволяет драйверу использовать преимущества кэширования метаданных.

Параметры безопасности Always Encrypted

Принудительное шифрование столбцов

Чтобы применить шифрование параметра, задайте значение для поля SQL_CA_SS_FORCE_ENCRYPT (дескриптор параметра реализации), вызвав функцию SQLSetDescField. Если это значение отличается от нуля, драйвер будет возвращать ошибку при отсутствии в ответе метаданных шифрования для соответствующего параметра.

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

Если SQL Server сообщает драйверу, что параметр не должен быть зашифрован, использующие этот параметр запросы будут завершаться ошибкой. Это поведение обеспечивает дополнительную защиту от атак на систему безопасности, включающих предоставление клиенту скомпрометированным SQL Server неверных метаданных шифрования, что может привести к раскрытию данных.

Кэширование ключа шифрования столбца

Чтобы уменьшить количество вызовов к хранилищу главных ключей столбцов для расшифровки ключей шифрования столбцов (CEK), драйвер кэширует ключи CEK в памяти в открытом тексте. Кэш CEK является глобальным для драйвера, а не связывается с отдельным подключением. Получив ECEK из метаданных базы данных, драйвер сначала пытается найти в кэше CEK в открытом тексте, который соответствует зашифрованному значению ключа. Драйвер обращается к хранилищу ключей, где хранится главный ключ столбца, только в том случае, если ему не удается найти в кэше значение CEK в открытом тексте.

Примечание.

Драйвер ODBC Driver for SQL Server вытесняет записи из этого кэша через два часа. Это означает, что для каждого ECEK драйвер обращается в хранилище ключей только один раз за цикл жизни приложения либо каждые два часа в зависимости от того, что меньше.

Начиная с драйвера ODBC Driver for SQL Server версии 17.1, время ожидания кэша для CEK можно изменить с помощью атрибута подключения SQL_COPT_SS_CEKCACHETTL, который указывает количество секунд хранения CEK в кэше. В силу глобальной природы кэша этот атрибут можно изменять из любого дескриптора подключения, который допустим для драйвера. При уменьшении значения срока жизни кэша из хранилища вытесняются все значения CEK, срок хранения которых не соответствует новому условию. Если задано значение 0, ключи CEK не кэшируются.

Доверенные пути ключей

Начиная с драйвера ODBC Driver for SQL Server версии 17.1, применяется атрибут подключения SQL_COPT_SS_TRUSTEDCMKPATHS, который позволяет приложению разрешить для операций Always Encrypted только конкретный список ключей CMK, который определяется по путям ключей. По умолчанию этот атрибут имеет значение NULL, и при этом драйвер принимает любой путь ключа. Чтобы использовать эту возможность, присвойте SQL_COPT_SS_TRUSTEDCMKPATHS нуль-терминированное строковое значение с расширенными символами, в котором перечислены разделенные нулевым значением допустимые пути ключей. Участки памяти, на которые ссылается этот атрибут, должны оставаться допустимыми на протяжении всех операций шифрования и расшифровки для того дескриптора подключения, в котором он задан. Драйвер будет постоянно проверять путь CMK, указанный в метаданных сервера, на наличие соответствующего значения в этом списке (без учета регистра). Если путь CMK отсутствует в этом списке, операция завершается ошибкой. Чтобы изменить список доверенных ключей CMK, приложение может изменять содержимое памяти, на которую указывает этот атрибут, не изменяя значение самого атрибута.

Работа с хранилищами главных ключей столбцов

Для шифрования или расшифровки данных драйвер должен получить ключ CEK, который настроен для целевого столбца. Ключи CEK хранятся в зашифрованном виде (ECEK) в метаданных базы данных. Каждому ключу CEK соответствует определенный ключ CMK, который использовался для его шифрования. Метаданные базы данных не хранят значения CMK, а только имя хранилища ключей и информацию, позволяющую найти нужные CMK в этом хранилище ключей.

Чтобы получить значение ECEK в открытом тексте, драйвер сначала получает метаданные о ключах CEK и CMK, а затем на основе этих сведений обращается в хранилище ключей, где содержится CMK, и запрашивает расшифрованную версию ECEK. Драйвер взаимодействует с хранилищем ключей через поставщик хранилища ключей.

Встроенные поставщики хранилища ключей

Драйвер ODBC Driver for SQL Server поставляется со следующими встроенными поставщиками хранилища ключей.

Имя Описание Имя (метаданные) поставщика Доступность
Azure Key Vault Хранит CMK в Azure Key Vault AZURE_KEY_VAULT Windows, macOS, Linux
Хранилище сертификатов Windows Хранит CMK локально в хранилище ключей Windows MSSQL_CERTIFICATE_STORE Windows
  • Вы (или ваш администратор баз данных) должны проверить правильность имени поставщика, настроенного в метаданных главного ключа столбца, и убедиться в том, что путь к ключу главного ключа столбца соответствует формату пути к ключу для данного поставщика. Для настройки ключей мы рекомендуем использовать специальные средства, например средство SQL Server Management Studio, которое при выполнении инструкции CREATE COLUMN MASTER KEY (Transact-SQL) автоматически создает допустимые имена поставщиков и пути к ключам.

  • Убедитесь, что приложение может получить доступ к ключу в хранилище ключей. Для этого процесса может потребоваться предоставить приложению доступ к ключу и (или) ко всему хранилищу ключей (в зависимости от хранилища ключей), либо выполнить другие действия по настройке для определенного хранилища ключей. Например, для доступа к Azure Key Vault необходимо предоставить правильные учетные данные для хранилища ключей.

Использование поставщика Azure Key Vault

Хранилище ключей Azure удобно использовать, чтобы хранить главные ключи столбцов для постоянного шифрования, особенно если приложения размещены в Azure. Драйвер ODBC Driver for SQL Server в Linux, macOS и Windows содержит встроенный поставщик хранилища ключей Azure Key Vault для хранения главного ключа. Дополнительные сведения о настройке Azure Key Vault для Always Encrypted вы найдете в этом пошаговом руководстве, в статье Об Azure Key Vault и разделе Создание главных ключей столбцов в хранилище ключей Azure.

Примечание.

Драйвер ODBC поддерживает проверку подлинности AKV непосредственно с идентификатором Microsoft Entra (ранее — Azure Active Directory). Если вы используете проверку подлинности Microsoft Entra для AKV и конфигурации требует проверки подлинности в службы федерации Active Directory (AD FS) конечной точке, проверка подлинности может завершиться ошибкой. В Linux и macOS, если используется драйвер версии 17.2 или более поздней, для использования этого поставщика требуется библиотека libcurl, но она не считается явной зависимостью, так как не нужна для других операций с драйвером. Если возникает связанная с пакетом libcurl ошибка, убедитесь в том, что он установлен.

Драйвер поддерживает проверку подлинности в Azure Key Vault со следующими типами учетных данных.

  • Имя пользователя и пароль — с помощью этого метода учетные данные — это имя пользователя Microsoft Entra и его пароль.

  • Идентификатор и секрет клиента — этот метод использует в качестве учетных данных идентификатор клиента приложения и секрет приложения.

  • Управляемое удостоверение (17.5.2 и выше) — системное или назначенное пользователем; дополнительные сведения см. в статье Управляемые удостоверения для ресурсов Azure.

  • Интерактивный azure Key Vault (17.7+ драйверы Windows) с помощью этого метода учетные данные проходят проверку подлинности с помощью идентификатора Microsoft Entra ID с идентификатором входа.

Чтобы разрешить драйверу использовать для шифрования столбцов ключи CMK, хранящиеся в Azure Key Vault, укажите в строке подключения следующие ключевые слова:

Тип учетных данных KeyStoreAuthentication KeyStorePrincipalId KeyStoreSecret
Имя пользователя/пароль KeyVaultPassword Имя субъекта-пользователя Password
Идентификатор и секрет клиента KeyVaultClientSecret Client ID Секретный
Управляемое удостоверение KeyVaultManagedIdentity Идентификатор объекта (необязательно, только для назначенного пользователем) (не указано)
Интерактивная AKV KeyVaultInteractive (не задано) (не задано)

Начиная с версии 17.8 KeystoreAuthentication и KeystorePrincipalId можно редактировать с помощью пользовательского интерфейса конфигурации DSN в администраторе источника данных ODBC.

Примеры строк подключения

Следующие строки подключения демонстрируют проверку подлинности в Azure Key Vault с двумя разными типами учетных данных.

Идентификатор и секрет клиента
"DRIVER=ODBC Driver 18 for SQL Server;SERVER=myServer;Encrypt=yes;Trusted_Connection=Yes;DATABASE=myDB;ColumnEncryption=Enabled;KeyStoreAuthentication=KeyVaultClientSecret;KeyStorePrincipalId=<clientId>;KeyStoreSecret=<secret>"
Имя пользователя или пароль
"DRIVER=ODBC Driver 18 for SQL Server;SERVER=myServer;Encrypt=yes;Trusted_Connection=Yes;DATABASE=myDB;ColumnEncryption=Enabled;KeyStoreAuthentication=KeyVaultPassword;KeyStorePrincipalId=<username>;KeyStoreSecret=<password>"
Управляемое удостоверение (назначенное системой)
"DRIVER=ODBC Driver 18 for SQL Server;SERVER=myServer;Encrypt=yes;Trusted_Connection=Yes;DATABASE=myDB;ColumnEncryption=Enabled;KeyStoreAuthentication=KeyVaultManagedIdentity"
Управляемое удостоверение (назначенное пользователем)
"DRIVER=ODBC Driver 18 for SQL Server;SERVER=myServer;Encrypt=yes;Trusted_Connection=Yes;DATABASE=myDB;ColumnEncryption=Enabled;KeyStoreAuthentication=KeyVaultManagedIdentity;KeyStorePrincipalId=<objectID>"
Интерактивная AKV
"DRIVER=ODBC Driver 18 for SQL Server;SERVER=myServer;Encrypt=yes;Trusted_Connection=Yes;DATABASE=myDB;ColumnEncryption=Enabled;KeyStoreAuthentication=KeyVaultInteractive;UID=<userID>;PWD=<password>"

Чтобы использовать Azure Key Vault в качестве хранилища для CMK не нужно вносить других изменений в приложение ODBC.

Примечание.

Драйвер содержит список конечных точек AKV, которым он доверяет. Начиная с версии 17.5.2 драйвера, этот список можно настроить: задайте для свойства AKVTrustedEndpoints в разделе реестра ODBCINST.INI или ODBC.INI драйвера или имени DSN (Windows) или в разделе файла odbcinst.ini или odbc.ini (Linux/macOS) список, разделенный точкой с запятой. Задание списка в имени DSN имеет приоритет над заданием списка в драйвере. Если значение начинается с точки с запятой, оно расширяет список по умолчанию; в противном случае оно заменяет список по умолчанию. Список по умолчанию (начиная с версии 17.5) — vault.azure.net;vault.azure.cn;vault.usgovcloudapi.net;vault.microsoftazure.de. Начиная с версии 17.7 в список также включается managedhsm.azure.net;managedhsm.azure.cn;managedhsm.usgovcloudapi.net;managedhsm.microsoftazure.de.

Примечание.

Встроенный в драйвер ODBC поставщик Azure Key Vault поддерживает и хранилища, и управляемые модули HSM в Azure Key Vault.

Использование поставщика хранилища сертификатов Windows

Драйвер ODBC Driver for SQL Server в Windows содержит встроенный поставщик MSSQL_CERTIFICATE_STORE хранилища сертификатов Windows для хранения главного ключа. (Этот поставщик недоступен для macOS и Linux.) При использовании этого поставщика ключи CMK хранятся локально на клиентском компьютере, и для его использования с драйвером не нужно вносить никаких дополнительных изменений в конфигурацию приложения. Но такому приложению необходимы права доступа к сертификату и закрытому ключу, размещенным в хранилище. Дополнительные сведения см. в разделе Create and Store Column Master Keys (Always Encrypted)(Создание и хранение главных ключей столбцов (постоянное шифрование)).

Использование пользовательских поставщиков хранилища ключей

Драйвер ODBC Driver for SQL Server также поддерживает пользовательские сторонние поставщики хранилища ключей через интерфейс CEKeystoreProvider. Это позволяет приложению загружать, запрашивать и настраивать поставщики хранилища ключей, чтобы драйвер мог использовать их для доступа к зашифрованным столбцам. Приложения могут напрямую взаимодействовать с поставщиком хранилища ключей, чтобы шифровать ключи CEK для хранения в SQL Server и выполнять другие задачи ODBC помимо доступа к зашифрованным столбцам. Дополнительные сведения см. в статье Пользовательские поставщики хранилища ключей.

Для взаимодействия с пользовательскими поставщиками хранилища ключей используются два атрибута подключения. В их число входят:

  • SQL_COPT_SS_CEKEYSTOREPROVIDER

  • SQL_COPT_SS_CEKEYSTOREDATA

Первый из них позволяет загружать и перечислять загруженные поставщики хранилища ключей, а второй нужен для взаимодействия между поставщиком и приложением. Эти атрибуты подключения можно применять в любое время, как до, так и после установки подключения, так как взаимодействие между поставщиком и приложением не требует обмена данными с SQL Server. Но перед подключением, когда драйвер еще не загружен, установка и получение этих атрибутов будет обрабатываться диспетчером драйверов и может приводить к неожиданным результатам.

Загрузка поставщика хранилища ключей

Настройка атрибута подключения SQL_COPT_SS_CEKEYSTOREPROVIDER позволяет клиентскому приложению загрузить библиотеку поставщика, что делает доступными для использования все содержащиеся в этой библиотеке поставщики хранилища ключей.

SQLRETURN SQLSetConnectAttr( SQLHDBC ConnectionHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength);
Аргумент Description
ConnectionHandle [Input] Дескриптор подключения Это должен быть допустимый дескриптор подключения, но загруженные через любой дескриптор подключения поставщики становятся доступны из любого дескриптора в том же процессе.
Attribute [Input] Атрибут для задания константы SQL_COPT_SS_CEKEYSTOREPROVIDER.
ValuePtr [Input] Указатель на нуль-терминированную строку, которая определяет имя файла библиотеки поставщика. Для SQLSetConnectAttrA этим значением является строка ANSI (multibyte). Для SQLSetConnectAttrW этим значением является строка Юникода (wchar_t).
StringLength [Input] Длина строки ValuePtr или SQL_NTS.

Драйвер пытается загрузить библиотеку, указанную в параметре ValuePtr, через определяемый платформой механизм загрузки динамических библиотек (dlopen() в Linux и macOS, LoadLibrary() в Windows) и добавляет все определенные в ней поставщики в список известных драйверу поставщиков. Могут возникать следующие ошибки.

Ошибка Описание
CE203 Не удалось загрузить динамическую библиотеку.
CE203 В библиотеке не удалось найти экспортированный символ CEKeyStoreProvider.
CE203 Один или несколько поставщиков из этой библиотеки уже загружены.

SQLSetConnectAttr возвращает обычные значения ошибки или успешного завершения, а дополнительные сведения по любым возникающим ошибкам можно получить через стандартный механизм диагностики ODBC.

Примечание.

Создателю приложения следует следить за тем, чтобы все пользовательские поставщики загружались до отправки использующих их запросов через любое подключение. Несоблюдение этого правила приведет к ошибке.

Ошибка Описание
CE200 Поставщик хранилища ключей %1 не найден. Убедитесь, что загружена соответствующая библиотека поставщика хранилища ключей.

Примечание.

Средства реализации поставщика хранилища ключей не должны использовать строку MSSQL в именах пользовательских поставщиков. Этот термин зарезервирован исключительно для корпорации Майкрософт, и его применение может привести к конфликту с будущими встроенными поставщиками. Наличие этого термина в имени пользовательского поставщика может создавать предупреждение ODBC.

Получение списка загруженных поставщиков

Получение этого атрибута подключения позволяет клиентскому приложению определить поставщиков хранилища ключей, загруженных в драйвер (включая встроенные поставщики). Этот процесс можно выполнить только после подключения.

SQLRETURN SQLGetConnectAttr( SQLHDBC ConnectionHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, SQLINTEGER * StringLengthPtr);
Аргумент Description
ConnectionHandle [Input] Дескриптор подключения Это должен быть допустимый дескриптор подключения, но загруженные через любой дескриптор подключения поставщики становятся доступны из любого дескриптора в том же процессе.
Attribute [Input] Атрибут для получения константы SQL_COPT_SS_CEKEYSTOREPROVIDER.
ValuePtr [Output] Указатель на участок памяти, в котором будет возвращено имя следующего загруженного поставщика.
BufferLength [Input] Длина буфера ValuePtr.
StringLengthPtr [Output] Указатель на буфер, в котором будет возвращено общее число байтов (за исключением нулевого символа завершения строки), доступных для получения из *ValuePtr. Если ValuePtr является пустым указателем, длина не возвращается. Если значением атрибута является строка символов, а количество байтов, доступных для возврата, превышает разницу между BufferLength и длиной нулевого завершающего символа, данные в *ValuePtr усекаются до BufferLength минус длина нулевого завершающего символа и завершается нулем драйвером.

Каждая операция Get возвращает имя текущего поставщика и увеличивает значение внутреннего счетчика до следующего поставщика, чтобы обеспечить возможность получить весь список. Когда этот счетчик достигнет конца списка, возвращается пустая строка ("") и счетчик сбрасывается. Следующая операция Get снова начинает процесс с начала списка.

Взаимодействие с поставщиками хранилища ключей

Атрибут подключения SQL_COPT_SS_CEKEYSTOREDATA позволяет клиентскому приложению взаимодействовать с загруженными поставщиками хранилища ключей, например для настройки дополнительных параметров, для передачи материала ключей и т. д. Взаимодействие между клиентским приложением и поставщиком выполняется по простому протоколу формата "запрос и ответ" через запросы Get и Set с помощью этого атрибута соединения. Взаимодействие всегда инициируется клиентским приложением.

Примечание.

В силу природы вызовов ODBC и реакции CEKeyStoreProvider на них (SQLGet/SetConnectAttr) интерфейс ODBC поддерживает задание данных с точностью до контекста подключения.

Приложение взаимодействует с поставщиками хранилища ключей через драйвер с использованием структуры CEKeystoreData:

typedef struct CEKeystoreData {
wchar_t *name;
unsigned int dataSize;
char data[];
} CEKEYSTOREDATA;
Аргумент Description
name [Input] В запросе Set содержит имя поставщика, которому отправляются данные. Не учитывается в запросе Get. Нуль-терминированная строка расширенных символов.
dataSize [Input] Размер массива данных в соответствии со структурой.
data [InOut] В запросе Set содержит данные для отправки поставщику. Это могут быть произвольные данные, драйвер не пытается их обрабатывать. В запросе Get указывает на буфер, в который будут помещаться полученные из поставщика данные.

Запись данных в поставщик

Вызов SQLSetConnectAttr с атрибутом SQL_COPT_SS_CEKEYSTOREDATA записывает "пакет" данных в указанный поставщик хранилища ключей.

SQLRETURN SQLSetConnectAttr( SQLHDBC ConnectionHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength);
Аргумент Description
ConnectionHandle [Input] Дескриптор подключения Это должен быть допустимый дескриптор подключения, но загруженные через любой дескриптор подключения поставщики становятся доступны из любого дескриптора в том же процессе.
Attribute [Input] Атрибут для задания константы SQL_COPT_SS_CEKEYSTOREDATA.
ValuePtr [Input] Указатель на структуру CEKeystoreData. Поле имени структуры идентифицирует поставщик, для которого предназначены данные.
StringLength [Input] Константа SQL_IS_POINTER

Подробные сведения об ошибке можно получить с помощью SQLGetDiacRec.

Примечание.

Поставщик может использовать дескриптор подключения для связи записываемых данных с конкретным подключением, если потребуется. Эта возможность полезна для реализации раздельных конфигураций для подключений. Также он может игнорировать контекст подключения и всегда использовать данные одинаково, независимо от подключения, через которые они отправлены. Дополнительные сведения см. в разделе о контексте связи.

Считывание данных из поставщика

Вызов SQLGetConnectAttr с атрибутом SQL_COPT_SS_CEKEYSTOREDATA считывает "пакет" данных из поставщика, в который осуществлялась последняя операция записи. Если операций записи еще не было, возникает ошибка последовательности функций. При реализации поставщиков хранилища ключей мы рекомендуем реализовать поддержку "фиктивных записей" длиной 0 байт, которые позволят выбирать поставщик для операций чтения без побочных эффектов, если такой сценарий имеет смысл.

SQLRETURN SQLGetConnectAttr( SQLHDBC ConnectionHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, SQLINTEGER * StringLengthPtr);
Аргумент Description
ConnectionHandle [Input] Дескриптор подключения Это должен быть допустимый дескриптор подключения, но загруженные через любой дескриптор подключения поставщики становятся доступны из любого дескриптора в том же процессе.
Attribute [Input] Атрибут для получения константы SQL_COPT_SS_CEKEYSTOREDATA.
ValuePtr [Output] Указатель на структуру CEKeystoreData, в которой размещаются считанные из поставщика данные.
BufferLength [Input] Константа SQL_IS_POINTER
StringLengthPtr [Output] Указатель на буфер, в котором возвращается значение BufferLength. Если *ValuePtr является пустым указателем, длина не возвращается.

Вызывающий объект должен выделить буфер достаточной длины для структуры CEKEYSTOREDATA, чтобы записать данные из поставщика. При завершении операции поле dataSize заполняется фактической длиной данных, считанных из поставщика. Подробные сведения об ошибке можно получить с помощью SQLGetDiacRec.

Этот интерфейс не предъявляет дополнительных требований к формату передачи данных между приложением и поставщиком хранилища ключей. Каждый поставщик может определить свой формат данных и протоколов в зависимости от конкретных потребностей.

Пример реализации поставщика хранилища ключей можно изучить в статье Custom Keystore Providers (Пользовательские поставщики хранилища ключей).

Ограничения драйвера ODBC при использовании Always Encrypted

Асинхронные операции

Драйвер ODBC разрешает использование асинхронных операций совместно с Always Encrypted, но такой режим использования негативно влияет на производительность операций. Вызов sys.sp_describe_parameter_encryption для определения метаданных шифрования для инструкции является блокирующим, то есть драйвер будет ждать получения метаданных от сервера, прежде чем возвращать SQL_STILL_EXECUTING.

Получение данных частями с помощью SQLGetData

В драйвере ODBC Driver for SQL Server версий старше 17 не было возможности получать по частям зашифрованные столбцы с символьными или двоичными данными через SQLGetData. Допускался только один вызов SQLGetData, для которого требовался буфер достаточной длины для размещения полных данных из столбца.

Отправка данных частями с помощью SQLGetData

В драйвере ODBC Driver for SQL Server версий старше 17.3 не было возможности отправлять частями данные для вставки или сравнения через SQLPutData. Допускался только один вызов SQLPutData с буфером, который содержит полные данные. Для вставки в зашифрованные столбцы длинных данных используйте интерфейс API массового копирования, который описан в следующем разделе, передавая ему файл входных данных.

Зашифрованные данные money и smallmoney

Шифрование столбцов типа money или smallmoney нельзя указать с помощью параметров, так как с этими типами не сопоставляется ни один тип данных ODBC. Подобная попытка приведет к ошибкам конфликта типов операндов.

Массовое копирование зашифрованных столбцов

Начиная с драйвера Microsoft ODBC Driver for SQL Server версии 17 для данных Always Encrypted поддерживаются функции массового копирования SQL и служебная программа bcp. Как открытый текст (зашифрованный при вставке и расшифрованный при извлечении), так и зашифрованный (переданный буквально) можно вставлять и извлекать с помощью API массового копирования (bcp_*) и утилиты bcp.

  • Чтобы получить зашифрованный текст в формате varbinary(max) (например, для массовой загрузки в другую базу данных), создайте подключение без параметра ColumnEncryption (или присвойте ему значение Disabled) и выполните операцию BCP OUT.

  • Чтобы вставка и извлечение выполнялись в открытом тексте, который драйвер будет прозрачно шифровать и расшифровывать, достаточно задать параметру ColumnEncryption значение Enabled. Все остальные функции API BCP сохраняются неизменными.

  • Чтобы вставка и извлечение зашифрованного текста выполнялись в формате varbinary(max) (как в примере выше), присвойте параметру BCPMODIFYENCRYPTED значение TRUE и выполните операцию BCP IN. Чтобы результирующие данные можно было расшифровать, ключ CEK для целевого столбца должен совпадать с тем ключом CEK, который изначально применялся для получения зашифрованного текста.

Если вы используете служебную программу bcp, для указания параметра ColumnEncryption следует указать аргумент -D и имя источника данных, содержащего нужное значение. Чтобы вставить зашифрованные данные, включите для пользователя параметр ALLOW_ENCRYPTED_VALUE_MODIFICATIONS.

Следующая таблица содержит краткое описание действий, доступных при работе с зашифрованным столбцом.

ColumnEncryption Направление BCP Description
Disabled Исходящее (к клиенту) Получение зашифрованного текста. Данные предоставляются в формате varbinary(max).
Enabled Исходящее (к клиенту) Получение открытого текста. Драйвер расшифрует данные столбца.
Disabled Входящее (к серверу) Вставка зашифрованного текста Этот параметр предназначен для непрозрачного перемещения зашифрованных данных, которые не нужно расшифровывать. Операция завершится ошибкой, если для пользователя не установлен параметр ALLOW_ENCRYPTED_VALUE_MODIFICATIONS или для дескриптора соединения не указан атрибут BCPMODIFYENCRYPTED. Дополнительные сведения см. в описании параметра ниже.
Enabled Входящее (к серверу) Вставка обычного текста. Драйвер зашифрует данные столбца.

Параметр BCPMODIFYENCRYPTED

Чтобы предотвратить повреждение данных, сервер обычно не допускает прямую вставку зашифрованных данных в зашифрованный столбец, то есть такая попытка завершится ошибкой. Но для массовой загрузки зашифрованных данных с помощью API BCP можно установить для параметра BCPMODIFYENCRYPTEDbcp_control значение TRUE, что разрешает прямую вставку зашифрованных данных и снижает риск их повреждения по сравнению с применением параметра ALLOW_ENCRYPTED_VALUE_MODIFICATIONS для учетной записи пользователя. Но при этом ключи должны соответствовать данным. Мы рекомендуем выполнить несколько проверок чтением после массовой вставки данных и перед их дальнейшим использованием.

Дополнительные сведения см. в разделе Перенос конфиденциальных данных с помощью функции Always Encrypted.

Сводные данные по API Always Encrypted

Ключевые слова в строке подключения

Имя Описание
ColumnEncryption Допустимые значения: Enabled/Disabled.
Enabled — включает функцию Always Encrypted для подключения.
Disabled — отключает функцию Always Encrypted для подключения.
attestation protocol, attestation URL (версия 17.4 и выше) — включает Always Encrypted с безопасным анклавом, используя указанные здесь протокол и URL-адрес аттестации.

Значение по умолчанию — Disabled.
KeyStoreAuthentication Допустимые значения: KeyVaultPassword, KeyVaultClientSecret, KeyVaultInteractive, KeyVaultManagedIdentity
KeyStorePrincipalId Если KeyStoreAuthentication = KeyVaultPasswordприсвойте этому значению допустимое имя участника-пользователя Microsoft Entra.
Если KeyStoreAuthetication = KeyVaultClientSecret задать это значение допустимым идентификатором клиента приложения Microsoft Entra
Если KeyStoreAuthetication = KeyVaultManagedIdentityприсвойте этому значению идентификатор объекта управляемого удостоверения, назначаемого пользователем. Если значение не указано, используется управляемое удостоверение, назначаемое системой.
KeyStoreSecret Если KeyStoreAuthentication = KeyVaultPassword, укажите здесь пароль для соответствующего имени пользователя.
Если KeyStoreAuthentication = KeyVaultClientSecret задать это значение секрету приложения, связанному с допустимым идентификатором клиента приложения Microsoft Entra

Атрибуты подключения

Имя. Тип Описание
SQL_COPT_SS_COLUMN_ENCRYPTION Перед подключением SQL_COLUMN_ENCRYPTION_DISABLE (0) — отключение always Encrypted
SQL_COLUMN_ENCRYPTION_ENABLE (1) — функция Always Encrypted включена.
Указатель на строку *attestation protocol*,*attestation URL* (версия 17.4 и более поздняя) — включено с безопасным анклавом.
SQL_COPT_SS_CEKEYSTOREPROVIDER После подключения [Set] Попытка загрузки CEKeystoreProvider
[Get] Возвращение имени CEKeystoreProvider
SQL_COPT_SS_CEKEYSTOREDATA После подключения [Set] Запись данных в CEKeystoreProvider
[Get] Чтение данных из CEKeystoreProvider
SQL_COPT_SS_CEKCACHETTL После подключения [Set] Указание срока жизни для ключей CEK в кэше
[Set] Получение текущего значения срока жизни для ключей CEK в кэше
SQL_COPT_SS_TRUSTEDCMKPATHS После подключения [Set] Сохранение указателя на доверенные пути CMK
[Get] Получение текущего указателя на доверенные пути CMK

Атрибуты инструкции

Имя Описание
SQL_SOPT_SS_COLUMN_ENCRYPTION SQL_CE_DISABLED (0) — Always Encrypted отключен для инструкции
SQL_CE_RESULTSETONLY (1) — только расшифровка. Результирующие наборы и возвращаемые значения расшифровываются, а параметры не шифруются
SQL_CE_ENABLED (3) — функция Always Encrypted включена и используется как для параметров, так и для результатов.

Поля дескриптора

Поле дескриптора параметра реализации (IPD) Размер и тип Значение по умолчанию Description
SQL_CA_SS_FORCE_ENCRYPT (1236) Машинное слово (2 байта) 0 Значение 0 (по умолчанию): решение о шифровании этого параметра определяется наличием метаданных шифрования.

Отличное от нуля значение: параметр шифруется, если для него присутствуют метаданные шифрования. В противном случае запрос завершается ошибкой [CE300] [Microsoft][ODBC Driver 17 for SQL Server] "Для параметра указано обязательное шифрование, но сервер не предоставил метаданные шифрования".

Параметры bcp_control

Имя параметра Значение по умолчанию Description
BCPMODIFYENCRYPTED (21) FALSE Если значение равно TRUE, в зашифрованный столбец можно вставлять значения в формате varbinary(max). Если значение равно FALSE, вставка не допускается без указания правильных метаданных типа и шифрования.

Устранение неполадок

При возникновении трудностей с использованием Always Encrypted сначала проверьте следующие моменты:

  • Наличие и доступность на сервере ключа CEK, который шифрует нужный столбец.

  • Наличие ключа CMK, который шифрует ключ CEK, имеет доступные метаданные на сервере, а также доступен из клиента.

  • ColumnEncryption включается в DSN, строке подключения или атрибуте подключения, а при использовании безопасного анклава имеет правильный формат.

Кроме того, при использовании безопасного анклава ошибки аттестации указывают на шаг в процессе аттестации, на котором произошел сбой (см. следующую таблицу).

Этап Description
0–99 Недопустимый ответ аттестации или ошибка при проверке подписи.
100–199 Ошибка при получении сертификатов из URL-адреса аттестации. Убедитесь, что <attestation URL>/v2.0/signingCertificates является допустимым и доступным.
200–299 Непредвиденный или недопустимый формат идентификатора анклава.
300–399 Ошибка при установке безопасного канала с анклавом.

См. также