Udostępnij przez


Używanie funkcji Always Encrypted z dostawcą danych microsoft .NET dla programu SQL Server

Dotyczy: .NET Framework .NET Standard

Ten artykuł zawiera informacje na temat tworzenia aplikacji .NET przy użyciu funkcji Always Encrypted lub Always Encrypted z bezpiecznymi enklawami i dostawcą danych microsoft .NET dla programu SQL Server.

Funkcja Always Encrypted umożliwia aplikacjom klienckim szyfrowanie poufnych danych i nigdy nie ujawnia danych ani kluczy szyfrowania w programie SQL Server lub usłudze Azure SQL Database. Sterownik z włączoną funkcją Always Encrypted, taki jak dostawca danych microsoft .NET dla programu SQL Server, zapewnia to bezpieczeństwo przez przezroczyste szyfrowanie i odszyfrowywanie poufnych danych w aplikacji klienckiej. Sterownik automatycznie określa, które parametry zapytania odpowiadają poufnym kolumnom bazy danych (chronionym przy użyciu funkcji Always Encrypted) i szyfruje wartości tych parametrów przed przekazaniem danych do serwera. Podobnie, sterownik odszyfrowuje w sposób niewidoczny dane pobrane z zaszyfrowanych kolumn bazy danych, znajdujące się w wynikach zapytania. Aby uzyskać więcej informacji, zobacz Tworzenie aplikacji przy użyciu funkcji Always Encrypted oraz Tworzenie aplikacji przy użyciu funkcji Always Encrypted z bezpiecznymi enklawami.

Wymagania wstępne

Poniższa tabela zawiera podsumowanie wymaganych platform .NET do używania funkcji Always Encrypted z elementem Microsoft.Data.SqlClient.

Obsługa funkcji Always Encrypted Obsługa funkcji Always Encrypted z bezpieczną enklawą Struktura docelowa Microsoft.Data.SqlClient, wersja programu System operacyjny
Tak Tak .NET Framework 4.6+ 1.1.0+ Windows
Tak Tak .NET Core 2.1+ 2.1.0+1 Windows, Linux, macOS
Tak Nie. .NET Standard 2.0 2.1.0+ Windows, Linux, macOS
Tak Tak .NET Standard 2.1+ 2.1.0+ Windows, Linux, macOS

Uwaga / Notatka

1 Przed microsoft.Data.SqlClient w wersji 2.1.0 funkcja Always Encrypted jest obsługiwana tylko w systemie Windows.

Włączanie funkcji Always Encrypted dla zapytań aplikacji

Najprostszym sposobem włączenia szyfrowania parametrów i odszyfrowania wyników zapytania dotyczących zaszyfrowanych kolumn jest ustawienie wartości słowa kluczowego Column Encryption Setting ciągu połączenia na włączone.

W poniższym przykładzie użyto parametrów połączenia, które umożliwiają funkcję Always Encrypted:

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

Poniższy fragment kodu jest odpowiednikiem przykładu przy użyciu właściwości SqlConnectionStringBuilder.ColumnEncryptionSetting.

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

Funkcję Always Encrypted można również włączyć dla poszczególnych zapytań. Zobacz sekcję Kontrolowanie wpływu funkcji Always Encrypted na wydajność poniżej. Włączenie funkcji Always Encrypted nie jest wystarczające do pomyślnego szyfrowania ani odszyfrowywania. Należy również upewnić się, że:

  • Aplikacja ma uprawnienia bazy danych VIEW ANY COLUMN MASTER KEY DEFINITION (WYŚWIETL DOWOLNĄ DEFINICJĘ KLUCZA GŁÓWNEGO COLUMN) i VIEW ANY COLUMN ENCRYPTION KEY DEFINITION (WYŚWIETL DOWOLNĄ DEFINICJĘ KLUCZA SZYFROWANIA KOLUMNY), wymagane do dostępu do metadanych dotyczących kluczy Always Encrypted w bazie danych. Aby uzyskać szczegółowe informacje, zobacz sekcję Uprawnienia bazy danych w temacie Always Encrypted (aparat bazy danych).
  • Aplikacja może uzyskać dostęp do klucza głównego kolumny, który chroni klucze szyfrowania kolumn, które szyfrują kwerendy kolumn bazy danych.

Włączanie funkcji Always Encrypted z bezpiecznymi enklawami

Począwszy od microsoft.Data.SqlClient w wersji 1.1.0 sterownik obsługuje funkcję Always Encrypted z bezpiecznymi enklawami.

Aby uzyskać ogólne informacje na temat tworzenia aplikacji przy użyciu enklaw, zobacz Develop applications using Always Encrypted with secure enlaves (Opracowywanie aplikacji przy użyciu funkcji Always Encrypted z bezpiecznymi enklawami).

Aby włączyć obliczenia enklawy dla połączenia z bazą danych, należy ustawić następujące słowa kluczowe parametrów połączenia, oprócz włączenia funkcji Always Encrypted (zgodnie z wyjaśnieniem w poprzedniej sekcji):

  • Attestation Protocol - określa protokół zaświadczania.

    • Jeśli to słowo kluczowe nie jest określone, bezpieczne enklawy są wyłączone na połączeniu.
    • Jeśli używasz programu SQL Server z enklawami zabezpieczeń opartymi na wirtualizacji (VBS) i usługą Ochrona hosta (HGS), wartość tego słowa kluczowego powinna mieć wartość HGS.
    • Jeśli używasz usługi Azure SQL Database z enklawami Intel SGX i zaświadczeniem platformy Microsoft Azure, wartość tego słowa kluczowego powinna mieć wartość AAS.
    • Jeśli używasz usługi Azure SQL Database lub programu SQL Server z enklawami VBS i chcesz zrezygnować z atestacji, wartość tego słowa kluczowego powinna wynosić None. Wymaga wersji 4.1 lub nowszej.

    Uwaga / Notatka

    "Brak" (bez zaświadczania) jest jedyną opcją obecnie obsługiwaną dla enklaw VBS w usłudze Azure SQL Database.

  • Enclave Attestation URL — określa adres URL zaświadczania (punkt końcowy usługi zaświadczania). Musisz uzyskać adres URL dla potrzeby atestacji swojego środowiska od administratora usługi atestacyjnej.

Aby zapoznać się z samouczkiem krok po kroku, zobacz Samouczek: tworzenie aplikacji .NET przy użyciu funkcji Always Encrypted z bezpiecznymi enklawami.

Pobieranie i modyfikowanie danych w zaszyfrowanych kolumnach

Po włączeniu funkcji Always Encrypted dla zapytań aplikacji można użyć standardowych interfejsów API sqlClient (zobacz Pobieranie i modyfikowanie danych w ADO.NET) lub dostawcy danych microsoft .NET dla interfejsów API programu SQL Server zdefiniowanych w przestrzeni nazw Microsoft.Data.SqlClient, aby pobrać lub zmodyfikować dane w zaszyfrowanych kolumnach bazy danych. Jeśli aplikacja ma wymagane uprawnienia bazy danych i może uzyskać dostęp do klucza głównego kolumny, dostawca danych platformy Microsoft .NET dla programu SQL Server zaszyfruje wszystkie parametry zapytania przeznaczone dla zaszyfrowanych kolumn i odszyfruje dane pobrane z zaszyfrowanych kolumn, zwracając wartości w postaci zwykłego tekstu typów platformy .NET odpowiadających typom danych programu SQL Server ustawionym dla kolumn w schemacie bazy danych. Jeśli funkcja Always Encrypted nie jest włączona, zapytania z parametrami skierowanymi do zaszyfrowanych kolumn nie powiodą się. Zapytania nadal mogą pobierać dane z zaszyfrowanych kolumn, o ile zapytanie nie ma parametrów przeznaczonych dla zaszyfrowanych kolumn. Jednak dostawca danych microsoft .NET dla programu SQL Server nie będzie próbował odszyfrować żadnych wartości pobranych z zaszyfrowanych kolumn, a aplikacja otrzyma zaszyfrowane dane binarne (jako tablice bajtów).

Poniższa tabela zawiera podsumowanie zachowania zapytań w zależności od tego, czy funkcja Always Encrypted jest włączona, czy nie:

Charakterystyka zapytania Funkcja Always Encrypted jest włączona, a aplikacja może uzyskiwać dostęp do kluczy i metadanych klucza Funkcja Always Encrypted jest włączona, a aplikacja nie może uzyskać dostępu do kluczy ani metadanych klucza Funkcja Always Encrypted jest wyłączona
Zapytania z parametrami przeznaczonymi dla zaszyfrowanych kolumn. Wartości parametrów są szyfrowane w sposób niewidoczny. Error Error
Zapytania pobierające dane z zaszyfrowanych kolumn bez użycia parametrów przeznaczonych dla tych kolumn. Wyniki z zaszyfrowanych kolumn są odszyfrowywane w sposób przejrzysty. Aplikacja otrzymuje wartości w postaci zwykłego tekstu typów danych platformy .NET odpowiadających typom programu SQL Server skonfigurowanym dla zaszyfrowanych kolumn. Error Wyniki z zaszyfrowanych kolumn nie są odszyfrowywane. Aplikacja odbiera zaszyfrowane wartości jako tablice bajtów (bajt[]).

Poniższe przykłady ilustrują pobieranie i modyfikowanie danych w zaszyfrowanych kolumnach. W przykładach przyjęto założenie, że tabela docelowa ma poniższy schemat. Kolumny SSN i BirthDate są szyfrowane.

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

Przykład dodawania danych

Ten przykład wstawia wiersz do tabeli Pacjentów. Zwróć uwagę na następujące szczegóły:

  • W przykładowym kodzie nie ma niczego specyficznego dla szyfrowania. Dostawca danych Microsoft .NET dla programu SQL Server automatycznie wykrywa i szyfruje parametry paramSSN i paramBirthdate przeznaczone dla zaszyfrowanych kolumn. To zachowanie sprawia, że szyfrowanie jest niewidoczne dla aplikacji.
  • Wartości wstawione do kolumn bazy danych, w tym zaszyfrowane kolumny, są przekazywane jako obiekty SqlParameter . Chociaż używanie parametru SqlParameter jest opcjonalne podczas wysyłania wartości do nieszyfrowanych kolumn (chociaż zdecydowanie jest zalecane, ponieważ pomaga zapobiegać wstrzyknięciu kodu SQL), jest to wymagane w przypadku wartości przeznaczonych dla zaszyfrowanych kolumn. Jeśli wartości wstawione w SSN kolumnach lub BirthDate zostały przekazane jako literały osadzone w instrukcji zapytania, zapytanie zakończy się niepowodzeniem, ponieważ dostawca danych microsoft .NET dla programu SQL Server nie będzie mógł określić wartości w kolumnach zaszyfrowanych docelowych, więc nie będzie szyfrować wartości. W związku z tym serwer odrzuci je jako niezgodne z zaszyfrowanymi kolumnami.
  • Typ danych parametru przeznaczonego dla kolumny SSN jest ustawiony na ciąg ANSI (non-Unicode), który jest mapowany do typu danych char/varchar w SQL Server. Jeśli typ parametru został ustawiony na ciąg Unicode (Ciąg), który jest mapowany na nchar/nvarchar, zapytanie zakończy się niepowodzeniem, ponieważ funkcja Always Encrypted nie obsługuje konwersji z zaszyfrowanych wartości nchar/nvarchar na zaszyfrowane wartości char/varchar. Zobacz Mapowania typów danych programu SQL Server , aby uzyskać informacje o mapowaniach typów danych.
  • Typ danych parametru wstawionego do BirthDate kolumny jest jawnie ustawiony na docelowy typ danych programu SQL Server przy użyciu właściwości SqlParameter.SqlDbType, zamiast polegać na niejawnym mapowaniu typów platformy .NET na typy danych programu SQL Server stosowane podczas korzystania z właściwości SqlParameter.DbType. Domyślnie struktura DateTime jest mapowana na typ danych datetime w programie SQL Server. Ponieważ typ BirthDate danych kolumny to data, a funkcja Always Encrypted nie obsługuje konwersji zaszyfrowanych wartości daty/godziny na zaszyfrowane wartości daty, użycie mapowania domyślnego spowodowałoby błąd.
string connectionString = "Data Source=server63; Initial Catalog=Clinic; Integrated Security=true; Column Encryption Setting=enabled";

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

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

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

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

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

    cmd.ExecuteNonQuery();
}

Przykład pobierania danych w postaci zwykłego tekstu

W poniższym przykładzie pokazano filtrowanie danych na podstawie zaszyfrowanych wartości i pobieranie danych w postaci zwykłego tekstu z zaszyfrowanych kolumn.

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

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

Uwaga / Notatka

  • Wartość użyta w klauzuli WHERE do filtrowania SSN kolumny musi zostać przekazana przy użyciu parametru SqlParameter, aby dostawca danych microsoft .NET dla programu SQL Server mógł w sposób niewidoczny zaszyfrować go przed wysłaniem go do bazy danych.

  • Wszystkie wartości drukowane przez program będą w postaci zwykłego tekstu, ponieważ Dostawca danych Microsoft .NET dla SQL Server w sposób przezroczysty odszyfruje dane pobrane z kolumn SSN i BirthDate.

  • Zapytania mogą wykonywać porównania równości w kolumnach, jeśli są szyfrowane przy użyciu szyfrowania deterministycznego.

Przykład pobierania zaszyfrowanych danych

Jeśli funkcja Always Encrypted nie jest włączona, zapytanie nadal może pobierać dane z zaszyfrowanych kolumn, o ile zapytanie nie ma parametrów przeznaczonych dla zaszyfrowanych kolumn.

W poniższym przykładzie pokazano, jak pobrać zaszyfrowane dane binarne z zaszyfrowanych kolumn.

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

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

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

Uwaga / Notatka

  • Ponieważ funkcja Always Encrypted nie jest włączona w parametrach połączenia, zapytanie zwróci zaszyfrowane wartości SSN i BirthDate jako tablice bajtów (program konwertuje wartości na ciągi).

  • Zapytanie pobierania danych z zaszyfrowanych kolumn z wyłączoną funkcją Always Encrypted może mieć parametry, o ile żaden z parametrów nie jest przeznaczony dla zaszyfrowanej kolumny. Powyższe zapytanie filtruje według wartości LastName, która nie jest szyfrowana w bazie danych. Jeśli zapytanie byłoby filtrowane według SSN lub BirthDate, zapytanie zakończyłoby się niepowodzeniem.

Unikanie typowych problemów podczas wykonywania zapytań dotyczących zaszyfrowanych kolumn

W tej sekcji opisano typowe kategorie błędów podczas wykonywania zapytań dotyczących zaszyfrowanych kolumn z aplikacji platformy .NET oraz kilka wskazówek dotyczących sposobu ich unikania.

Błędy konwersji nieobsługiwanego typu danych

Funkcja Always Encrypted obsługuje kilka konwersji dla zaszyfrowanych typów danych. Zobacz Always Encrypted w celu uzyskania szczegółowej listy obsługiwanych konwersji typów. Wykonaj następujące czynności, aby uniknąć błędów konwersji typu danych:

  • Ustaw typy parametrów przeznaczonych dla zaszyfrowanych kolumn, aby typ danych parametru w programie SQL Server był dokładnie taki sam jak typ kolumny docelowej lub aby była obsługiwana konwersja typu danych parametru w programie SQL Server na typ kolumny docelowej. Żądane mapowanie typów danych platformy .NET można wymusić na określone typy danych programu SQL Server przy użyciu właściwości SqlParameter.SqlDbType.
  • Sprawdź, czy dokładność i skala parametrów określania wartości docelowych kolumn typów danych programu SQL Server dziesiętnych i liczbowych jest taka sama jak precyzja i skala skonfigurowana dla kolumny docelowej.
  • Sprawdź, czy dokładność parametrów określania wartości docelowych kolumn datetime2, datetimeoffset lub time typów danych programu SQL Server nie jest większa niż precyzja kolumny docelowej (w zapytaniach modyfikujących wartości w kolumnie docelowej).

Błędy spowodowane przekazywaniem zwykłego tekstu zamiast zaszyfrowanych wartości

Każda wartość docelowa zaszyfrowanej kolumny musi być zaszyfrowana wewnątrz aplikacji. Próba wstawienia/zmodyfikowania lub filtrowania według wartości zwykłego tekstu w zaszyfrowanej kolumnie spowoduje błąd podobny do następującego:

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

Aby zapobiec takim błędom, upewnij się, że:

  • Funkcja Always Encrypted jest włączona dla zapytań aplikacji przeznaczonych dla zaszyfrowanych kolumn (dla parametrów połączenia lub w obiekcie SqlCommand dla określonego zapytania).
  • Funkcja SqlParameter służy do wysyłania danych przeznaczonych dla zaszyfrowanych kolumn. W poniższym przykładzie pokazano zapytanie, które niepoprawnie filtruje według literału/stałej w zaszyfrowanej kolumnie (SSN) zamiast przekazywać literał wewnątrz obiektu SqlParameter.
using (SqlCommand cmd = connection.CreateCommand())
{
    cmd.CommandText = @"SELECT [SSN], [FirstName], [LastName], [BirthDate] FROM [dbo].[Patients] WHERE SSN = '795-73-9838'";
    cmd.ExecuteNonQuery();
}

Praca z repozytoriami kluczy głównych kolumn

Aby zaszyfrować wartość parametru lub odszyfrować dane w wynikach zapytania, dostawca danych microsoft .NET dla programu SQL Server musi uzyskać klucz szyfrowania kolumny skonfigurowany dla kolumny docelowej. Klucze szyfrowania kolumn są przechowywane w postaci zaszyfrowanej w metadanych bazy danych. Każdy klucz szyfrowania kolumny posiada odpowiadający mu klucz główny kolumny, użyty do jego zaszyfrowania. Metadane bazy danych nie przechowują kluczy głównych kolumn — zawiera tylko informacje o magazynie kluczy zawierającym określony klucz główny kolumny i lokalizację klucza w magazynie kluczy.

Aby uzyskać wartość zwykłego tekstu klucza szyfrowania kolumny, dostawca danych microsoft .NET dla programu SQL Server najpierw uzyskuje metadane dotyczące zarówno klucza szyfrowania kolumny, jak i odpowiadającego mu klucza głównego kolumny. Następnie używa informacji w metadanych, aby skontaktować się z magazynem kluczy zawierającym klucz główny kolumny i odszyfrować zaszyfrowany klucz szyfrowania kolumny. Dostawca danych Microsoft .NET dla programu SQL Server komunikuje się z magazynem kluczy przy użyciu dostawcy magazynu kluczy głównych kolumn, czyli instancji klasy pochodnej od klasy SqlColumnEncryptionKeyStoreProvider.

Proces uzyskiwania klucza szyfrowania kolumny:

  1. Jeśli funkcja Always Encrypted jest włączona dla zapytania, dostawca danych microsoft .NET dla programu SQL Server w sposób niewidoczny wywołuje sys.sp_describe_parameter_encryption w celu pobrania metadanych szyfrowania dla parametrów przeznaczonych dla zaszyfrowanych kolumn, jeśli zapytanie ma parametry. W przypadku zaszyfrowanych danych zawartych w wynikach zapytania program SQL Server automatycznie dołącza metadane szyfrowania. Informacje o kluczu głównym kolumny obejmują:

    • Nazwa dostawcy magazynu kluczy, który hermetyzuje magazyn kluczy zawierający klucz główny kolumny.
    • Ścieżka klucza określająca lokalizację klucza głównego kolumny w magazynie kluczy.

    Informacje o kluczu szyfrowania kolumny obejmują:

    • Zaszyfrowana wartość klucza szyfrowania kolumny.
    • Nazwa algorytmu używanego do szyfrowania klucza szyfrowania kolumny.
  2. Dostawca danych microsoft .NET dla programu SQL Server używa nazwy dostawcy magazynu kluczy głównych kolumn do wyszukiwania obiektu dostawcy, który jest wystąpieniem klasy pochodzącej z klasy SqlColumnEncryptionKeyStoreProvider w wewnętrznej strukturze danych.

  3. Aby odszyfrować klucz szyfrowania kolumny, dostawca danych microsoft .NET dla programu SQL Server wywołuje SqlColumnEncryptionKeyStoreProvider.DecryptColumnEncryptionKey() metodę, przekazując ścieżkę klucza głównego kolumny, zaszyfrowaną wartość klucza szyfrowania kolumny i nazwę algorytmu szyfrowania używanego do tworzenia zaszyfrowanego klucza szyfrowania kolumny.

Korzystanie z wbudowanymi dostawcami magazynu kluczy głównych kolumn

Dostawca danych microsoft .NET dla programu SQL Server jest dostarczany z następującymi wbudowanymi dostawcami magazynu kluczy głównych kolumn, którzy są wstępnie zarejestrowani przy użyciu określonych nazw dostawców (używanych do wyszukiwania dostawcy). Ci wbudowani dostawcy magazynu kluczy są obsługiwani tylko w systemie Windows.

Class Description Nazwa dostawcy (odnośnika) Platforma
Klasa SqlColumnEncryptionCertificateStoreProvider Dostawca magazynu certyfikatów systemu Windows. MSSQL_CERTIFICATE_STORE Windows
Klasa SqlColumnEncryptionCngProvider Dostawca magazynu kluczy, który wspiera Microsoft Cryptography API: Next Generation (CNG) API. Zazwyczaj magazynem tego typu jest sprzętowy moduł zabezpieczeń — urządzenie fizyczne, które chroni klucze cyfrowe i zarządza nimi oraz zapewnia przetwarzanie kryptograficzne. MSSQL_CNG_STORE Windows
Klasa SqlColumnEncryptionCspProvider Dostawca magazynu kluczy, który obsługuje interfejs API kryptografii firmy Microsoft (CAPI). Zazwyczaj magazynem tego typu jest sprzętowy moduł zabezpieczeń — urządzenie fizyczne, które chroni klucze cyfrowe i zarządza nimi oraz zapewnia przetwarzanie kryptograficzne. MSSQL_CSP_PROVIDER Windows

Nie musisz wprowadzać żadnych zmian w kodzie aplikacji, aby korzystać z tych dostawców, ale należy pamiętać o następujących szczegółach:

Korzystanie z dostawcy usługi Azure Key Vault

Usługa Azure Key Vault to wygodna opcja przechowywania kluczy głównych kolumn dla funkcji Always Encrypted i zarządzania nimi (zwłaszcza jeśli aplikacje są hostowane na platformie Azure). Dostawca danych platformy Microsoft .NET dla programu SQL Server nie zawiera wbudowanego dostawcy magazynu kluczy głównych kolumn dla usługi Azure Key Vault, ale jest dostępny jako pakiet NuGet (Microsoft.Data.SqLClient.AlwaysEncrypted.AzureKeyVaultProvider), który można łatwo zintegrować z aplikacją. Aby uzyskać szczegółowe informacje, zobacz Always Encrypted — ochrona poufnych danych w usłudze SQL Database za pomocą szyfrowania danych i przechowywanie kluczy szyfrowania w usłudze Azure Key Vault.

Class Description Nazwa dostawcy (odnośnika) Platforma
Klasa SqlColumnEncryptionAzureKeyVaultProvider Dostawca usługi Azure Key Vault. AZURE_KEY_VAULT Windows, Linux, macOS

Obsługa platformy .NET

wersja Microsoft.Data.SqlClient, wersja: Platformy .NET
3.0.0 3.0.0+ .NET Framework 4.6.1+, .NET Core 2.1+, .NET Standard 2.0+
2.0.0 1.1.3+
2.1.0+
.NET Framework 4.6.1+, .NET Core 2.1+
.NET Standard 2.0+
1.2.0 1.0.19269.1+
2.1.0+
.NET Framework 4.6+, .NET Core 2.1+
.NET Standard 2.0+
1.1.0 1.0.19269.1+ .NET Framework 4.6+, .NET Core 2.1+
1.0.0 1.0.19269.1+ .NET Framework 4.6+, .NET Core 2.1+

Począwszy od wersji 3.0.0, Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider obsługuje funkcję buforowania klucza szyfrowania kolumny podczas rejestrowania dostawcy przy użyciu polecenia SqlConnection.RegisterColumnEncryptionKeyStoreProvidersOnConnection lub SqlCommand.RegisterColumnEncryptionKeyStoreProvidersOnCommand.

Począwszy od wersji 2.0.0, Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider obsługuje nowe interfejsy API Azure.Core i Azure.Identity w celu przeprowadzenia uwierzytelniania za pomocą usługi Azure Key Vault. Wystąpienie implementacji TokenCredential można teraz przekazać do SqlColumnEncryptionAzureKeyVaultProvider konstruktorów w celu zainicjowania obiektu dostawcy usługi Azure Key Vault.

Uwaga / Notatka

Usługa Microsoft.Data.SqLClient.AlwaysEncrypted.AzureKeyVaultProvider obsługuje zarówno Skarbce, jak i zarządzane moduły HSM w usłudze Azure Key Vault.

Przykłady demonstrujące szyfrowanie/odszyfrowywanie za pomocą usługi Azure Key Vault można znaleźć w artykułach Azure Key Vault working with Always Encrypted oraz Azure Key Vault working with Always Encrypted with secure enclaves.

Implementowanie niestandardowego dostawcy magazynu głównych kluczy kolumnowych

Jeśli chcesz przechowywać klucze główne kolumn w magazynie kluczy, który nie jest obsługiwany przez istniejącego dostawcę, możesz zaimplementować dostawcę niestandardowego, rozszerzając klasę SqlColumnEncryptionKeyStoreProvider i rejestrując dostawcę przy użyciu jednej z następujących metod:

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

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

Pierwszeństwo pamięci podręcznej dla klucza szyfrowania kolumn

Ta sekcja dotyczy wersji 3.0 i nowszej dostawcy danych microsoft .NET dla programu SQL Server.

Klucze szyfrowania kolumny (CEK) odszyfrowane przez niestandardowe dostawcy magazynów kluczy zarejestrowani w instancji połączenia lub polecenia nie będą buforowane przez Dostawcę Danych Microsoft .NET dla programu SQL Server. Niestandardowi dostawcy przechowywania kluczy powinni zaimplementować własny mechanizm buforowania CEK.

Począwszy od wersji 3.0.0 programu Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider, każde wystąpienie Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider programu ma własną implementację buforowania CEK. Po zarejestrowaniu w instancji połączenia lub polecenia, zestawy CEK odszyfrowane przez instancję Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider zostaną wyczyszczone, gdy zakres tej instancji dobiega końca.

class Program
{
    private static string connectionString;

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

Uwaga / Notatka

Buforowanie klucza CEK zaimplementowane przez niestandardowych dostawców magazynów kluczy zostanie wyłączone przez sterownik, jeśli wystąpienie dostawcy magazynu klucza jest zarejestrowane w sterowniku globalnie przy użyciu metody SqlConnection.RegisterColumnEncryptionKeyStoreProviders. Każda implementacja buforowania CEK powinna odwoływać się do wartości SqlColumnEncryptionKeyStoreProvider.ColumnEncryptionKeyCacheTtl przed buforowaniem klucza CEK, a nie buforować go, jeśli wartość jest równa zero. Pozwoli to uniknąć zduplikowanego buforowania i możliwych nieporozumień użytkownika podczas próby skonfigurowania buforowania kluczy.

Rejestrowanie niestandardowego dostawcy magazynu kluczy głównych kolumn

Ta sekcja dotyczy wersji 3.0 i wyższych dostawcy.

Niestandardowi dostawcy magazynu kluczy głównych mogą być zarejestrowani za pomocą sterownika w trzech różnych warstwach. Pierwszeństwo trzech rejestracji jest następujące:

  • Rejestracja na polecenie zostanie sprawdzona, czy nie jest pusta.
  • Jeśli rejestracja na polecenie jest pusta, rejestracja na połączenie zostanie sprawdzona, czy nie jest pusta.
  • Jeśli rejestracja na połączenie jest pusta, rejestracja globalna zostanie sprawdzona.

Po znalezieniu dowolnego dostawcy magazynu kluczy na poziomie rejestracji sterownik NIE wróci do innych rejestracji w celu wyszukania dostawcy. Jeśli dostawcy są zarejestrowani, ale odpowiedni dostawca nie zostanie znaleziony na danym poziomie, zostanie zgłoszony wyjątek zawierający wyłącznie zarejestrowanych dostawców, którzy byli uwzględnieni w sprawdzanej rejestracji.

Wbudowani dostawcy magazynu kluczy głównych kolumn, którzy są dostępni dla magazynu certyfikatów systemu Windows, magazynu CNG i dostawcy CSP są wstępnie zarejestrowane.

Trzy poziomy rejestracji obsługują różne scenariusze podczas wykonywania zapytań dotyczących zaszyfrowanych danych. Odpowiednią metodę można użyć, aby upewnić się, że użytkownik aplikacji może uzyskać dostęp do danych w postaci zwykłego tekstu, jeśli może podać wymagany klucz główny kolumny, uwierzytelniając się w magazynie kluczy zawierającym klucz główny kolumny.

Aplikacje współdzielące instancję SqlConnection między wieloma użytkownikami mogą chcieć użyć SqlCommand.RegisterColumnEncryptionKeyStoreProvidersOnCommand. Każdy użytkownik musi zarejestrować dostawcę magazynu kluczy w wystąpieniu sqlCommand przed wykonaniem zapytania w celu uzyskania dostępu do zaszyfrowanej kolumny. Jeśli dostawca magazynu kluczy może uzyskać dostęp w magazynie kluczy do wymaganego klucza głównego kolumny przy użyciu podanych poświadczeń użytkownika, zapytanie zakończy się sukcesem.

Aplikacje tworzące wystąpienie SqlConnection dla każdego użytkownika mogą chcieć użyć SqlConnection.RegisterColumnEncryptionKeyStoreProvidersOnConnection. Dostawcy magazynu kluczy zarejestrowani za pomocą tej metody mogą użyć połączenia dla dowolnego zapytania, które uzyskuje dostęp do zaszyfrowanych danych.

Dostawcy magazynu kluczy zarejestrowani przy użyciu programu SqlConnection.RegisterColumnEncryptionKeyStoreProviders będą używać tożsamości podanej przez aplikację podczas uwierzytelniania w magazynie kluczy.

W poniższym przykładzie pokazano priorytet dostawców niestandardowych magazynów kluczy głównych kolumn zarejestrowanych w instancji połączenia:

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

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

W poniższym przykładzie pokazano priorytet dostawców niestandardowego magazynu kluczy głównych kolumn, które są zarejestrowane w wystąpieniu polecenia.

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

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

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

Używanie dostawców magazynu kluczy głównych kolumn na potrzeby programistycznego przypisania kluczy

Gdy Microsoft .NET Data Provider for SQL Server uzyskuje dostęp do zaszyfrowanych kolumn, automatycznie odnajduje i wywołuje dostawcę magazynu klucza głównego w celu odszyfrowania kluczy kolumn. Zazwyczaj typowy kod aplikacji nie wywołuje bezpośrednio dostawców magazynów kluczy głównych kolumn. Możesz jednak utworzyć wystąpienie dostawcy i wywołać go jawnie, aby programowo utworzyć klucze Always Encrypted i zarządzać nimi: aby wygenerować zaszyfrowany klucz szyfrowania kolumny i odszyfrować klucz szyfrowania kolumny (na przykład jako część rotacji klucza głównego kolumny). Aby uzyskać więcej informacji, zobacz Omówienie zarządzania kluczami dla funkcji Always Encrypted. Implementacja własnych narzędzi do zarządzania kluczami może być wymagana tylko wtedy, gdy używasz niestandardowego dostawcy magazynu kluczy. W przypadku używania kluczy przechowywanych w magazynach kluczy, dla których istnieją wbudowani dostawcy i usługi Azure Key Vault, można użyć istniejących narzędzi, takich jak SQL Server Management Studio lub PowerShell, do zarządzania kluczami i ich aprowizowania. Poniższy przykład ilustruje generowanie klucza szyfrowania kolumny i używanie klasy SqlColumnEncryptionCertificateStoreProvider do szyfrowania klucza przy użyciu certyfikatu.

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

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

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

Kontrolowanie wpływu na wydajność funkcji Always Encrypted

Ponieważ funkcja Always Encrypted jest technologią szyfrowania po stronie klienta, większość obciążeń związanych z wydajnością jest obserwowana po stronie klienta, a nie w bazie danych. Oprócz kosztów operacji szyfrowania i odszyfrowywania inne źródła obciążenia wydajności po stronie klienta to:

  • Dodatkowe rundy do bazy danych w celu pobrania metadanych dla parametrów zapytania.
  • Wywołuje magazyn kluczy głównych kolumn w celu uzyskania dostępu do klucza głównego kolumny.

W tej sekcji opisano wbudowane optymalizacje wydajności w programie Microsoft .NET Data Provider dla programu SQL Server oraz sposób kontrolowania wpływu powyższych dwóch czynników na wydajność.

Kontrolowanie rund pobierania metadanych dla parametrów zapytania

Jeśli funkcja Always Encrypted jest domyślnie włączona dla połączenia, dostawca danych microsoft .NET dla programu SQL Server wywoła sys.sp_describe_parameter_encryption dla każdego zapytania sparametryzowanego, przekazując instrukcję zapytania (bez żadnych wartości parametrów) do programu SQL Server. sys.sp_describe_parameter_encryption analizuje instrukcję zapytania, aby dowiedzieć się, czy jakiekolwiek parametry muszą być zaszyfrowane, a jeśli tak, dla każdego z nich, zwraca informacje dotyczące szyfrowania, które umożliwią dostawcy danych microsoft .NET dla programu SQL Server szyfrowanie wartości parametrów. Powyższe zachowanie zapewnia wysoki poziom przejrzystości aplikacji klienckiej. Aplikacja (i deweloper aplikacji) nie musi wiedzieć, które zapytania uzyskują dostęp do zaszyfrowanych kolumn, o ile wartości przeznaczone dla zaszyfrowanych kolumn są przekazywane do dostawcy danych Microsoft .NET dla programu SQL Server w obiektach SqlParameter.

Buforowanie metadanych zapytań

Dostawca danych microsoft .NET dla programu SQL Server buforuje wyniki sys.sp_describe_parameter_encryption dla każdej instrukcji zapytania. Dlatego jeśli ta sama instrukcja zapytania jest wykonywana wiele razy, sterownik wywołuje sys.sp_describe_parameter_encryption tylko raz. Buforowanie metadanych szyfrowania dla instrukcji zapytań znacznie zmniejsza koszt wydajności pobierania metadanych z bazy danych. Buforowanie jest domyślnie włączone. Buforowanie metadanych parametrów można wyłączyć, ustawiając właściwość SqlConnection.ColumnEncryptionQueryMetadataCacheEnabled na false, ale nie jest to zalecane z wyjątkiem rzadkich przypadków, takich jak opisane poniżej:

Rozważmy bazę danych, która ma dwa różne schematy: s1 i s2. Każdy schemat zawiera tabelę o tej samej nazwie: t. Definicje tabel s1.t i s2.t są identyczne, z wyjątkiem właściwości związanych z szyfrowaniem: kolumna o nazwie c w tabeli s1.t nie jest szyfrowana, a w tabeli s2.t jest. Baza danych ma dwóch użytkowników: u1 i u2. Domyślnym schematem u1 dla użytkowników jest s1. Domyślny schemat dla elementu u2 to s2. Aplikacja .NET otwiera dwa połączenia z bazą danych, podszywając się pod użytkownika u1 w jednym połączeniu i pod użytkownika u2 w drugim połączeniu. Aplikacja wysyła zapytanie z parametrem określającym wartość docelową c kolumny za pośrednictwem połączenia użytkownika u1 (zapytanie nie określa schematu, więc przyjmuje się domyślny schemat użytkownika). Następnie aplikacja wysyła to samo zapytanie za pośrednictwem połączenia dla u2 użytkownika. Jeśli buforowanie metadanych zapytań jest włączone, po pierwszym zapytaniu pamięć podręczna zostanie wypełniona metadanymi, które wskazują, że kolumna c, na którą wskazuje parametr zapytania, nie jest zaszyfrowana. Ponieważ drugie zapytanie ma identyczną instrukcję zapytania, będą używane informacje przechowywane w pamięci podręcznej. W związku z tym sterownik wyśle zapytanie, nie zaszyfrowując parametru (co jest niepoprawne, ponieważ kolumna docelowa, s2.t.c, jest szyfrowana), co prowadzi do ujawnienia wartości jawnej parametru serwerowi. Serwer wykryje niezgodność i wymusi odświeżenie pamięci podręcznej przez sterownik, więc aplikacja w sposób przezroczysty ponownie wyśle zapytanie z poprawnie zaszyfrowaną wartością parametru. W takim przypadku buforowanie powinno być wyłączone, aby zapobiec wyciekowi poufnych wartości do serwera.

Ustawianie funkcji Always Encrypted na poziomie zapytania

Aby kontrolować wpływ wydajności pobierania metadanych szyfrowania dla sparametryzowanych zapytań, można włączyć funkcję Always Encrypted dla poszczególnych zapytań, zamiast konfigurować je dla połączenia. Dzięki temu można upewnić się, że sys.sp_describe_parameter_encryption jest wywoływana tylko dla zapytań, o których wiesz, że mają parametry przeznaczone do zaszyfrowanych kolumn. Należy jednak pamiętać, że dzięki temu można zmniejszyć przejrzystość szyfrowania: jeśli zmienisz właściwości szyfrowania kolumn bazy danych, może być konieczne zmianę kodu aplikacji, aby dopasować go do zmian schematu.

Uwaga / Notatka

Ustawienie funkcji Always Encrypted na poziomie zapytania ogranicza wydajność buforowania metadanych szyfrowania parametrów.

Aby kontrolować zachowanie funkcji Always Encrypted poszczególnych zapytań, należy użyć konstruktora SqlCommand i SqlCommandColumnEncryptionSetting. Oto kilka przydatnych wskazówek:

  • Jeśli większość zapytań wykonywanych przez aplikację kliencką uzyskuje dostęp do zaszyfrowanych kolumn:
    • Ustaw słowo kluczowe Ustawienie szyfrowania kolumny w parametrze połączenia na Włączone.
    • Ustaw wartość SqlCommandColumnEncryptionSetting na wyłączone dla poszczególnych zapytań, które nie uzyskują dostępu do żadnych zaszyfrowanych kolumn. To ustawienie spowoduje wyłączenie wywoływania sys.sp_describe_parameter_encryption i próby odszyfrowania wszystkich wartości w zestawie wyników.
    • Ustaw wartość SqlCommandColumnEncryptionSetting na ResultSetOnly dla poszczególnych zapytań, które nie mają żadnych parametrów wymagających szyfrowania, ale pobieraj dane z zaszyfrowanych kolumn. To ustawienie spowoduje wyłączenie wywoływania sys.sp_describe_parameter_encryption i szyfrowania parametrów. Zapytanie będzie mogło odszyfrować wyniki z kolumn szyfrowania.
  • Jeśli większość zapytań wykonywanych przez aplikację kliencką nie uzyskuje dostępu do zaszyfrowanych kolumn:
    • Ustaw słowo kluczowe parametru połączenia Ustawienie szyfrowania kolumn na Disabled.
    • Ustaw wartość SqlCommandColumnEncryptionSetting na włączoną dla poszczególnych zapytań, które mają wszystkie parametry, które muszą być szyfrowane. To ustawienie umożliwi wywoływanie sys.sp_describe_parameter_encryption i odszyfrowywanie wszystkich wyników zapytania pobranych z zaszyfrowanych kolumn.
    • Ustaw wartość SqlCommandColumnEncryptionSetting na ResultSetOnly dla zapytań, które nie mają żadnych parametrów wymagających szyfrowania, ale pobiera dane z zaszyfrowanych kolumn. To ustawienie spowoduje wyłączenie wywoływania sys.sp_describe_parameter_encryption i szyfrowania parametrów. Zapytanie będzie mogło odszyfrować wyniki z kolumn szyfrowania.

W poniższym przykładzie funkcja Always Encrypted jest wyłączona dla połączenia z bazą danych. Zapytanie dotyczące problemów z aplikacją ma parametr, który jest przeznaczony dla kolumny LastName, która nie jest zaszyfrowana. Zapytanie pobiera dane z kolumn SSN i BirthDate, z których obie są szyfrowane. W takim przypadku wywołanie sys.sp_describe_parameter_encryption w celu pobrania metadanych szyfrowania nie jest wymagane. Należy jednak włączyć odszyfrowywanie wyników zapytania, aby aplikacja mogła odbierać wartości w postaci zwykłego tekstu z dwóch zaszyfrowanych kolumn. Ustawienie SqlCommandColumnEncryptionSettingResultSetOnly jest używane, aby to zapewnić.

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

Buforowanie klucza szyfrowania kolumn

Aby zmniejszyć liczbę wywołań do magazynu kluczy głównych kolumn w celu odszyfrowania kluczy szyfrowania kolumn, dostawca danych microsoft .NET dla programu SQL Server buforuje klucze szyfrowania kolumn w postaci zwykłego tekstu w pamięci. Po otrzymaniu przez dostawcę wartości klucza szyfrowania zaszyfrowanej kolumny z metadanych bazy danych sterownik najpierw próbuje znaleźć klucz szyfrowania kolumny w postaci zwykłego tekstu odpowiadający wartości zaszyfrowanego klucza. Sterownik wywołuje magazyn kluczy zawierający klucz główny kolumny tylko wtedy, gdy nie może znaleźć wartości zaszyfrowanego klucza szyfrowania kolumny w pamięci podręcznej.

Wpisy pamięci podręcznej są usuwane po konfigurowalnym okresie ważności ze względów bezpieczeństwa. Domyślna wartość czasu wygaśnięcia to 2 godziny. Jeśli masz bardziej rygorystyczne wymagania dotyczące zabezpieczeń dotyczące czasu buforowania kluczy szyfrowania kolumn w postaci zwykłego tekstu w aplikacji, możesz je zmienić przy użyciu właściwości SqlConnection.ColumnEncryptionKeyCacheTtl.

Niestandardowi dostawcy magazynu kluczy zarejestrowani za pomocą SqlConnection.RegisterColumnEncryptionKeyStoreProvidersOnConnection i SqlCommand.RegisterColumnEncryptionKeyStoreProvidersOnCommand nie będą mieli odszyfrowanych kluczy szyfrowania kolumn przechowywanych w pamięci podręcznej przez Microsoft .NET Data Provider for SQL Server. Zamiast tego dostawcy niestandardowych magazynów kluczy muszą zaimplementować własny mechanizm buforowania. Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider Wersja 3.0.0 i nowsze są dostarczane z własną implementacją buforowania.

Aby obsługiwać scenariusze, w których różni użytkownicy tej samej aplikacji mogą wykonywać wiele zapytań, niestandardowi dostawcy magazynu kluczy mogą być mapowani na użytkownika i rejestrowani na połączeniu lub wystąpieniu polecenia specyficznym dla tego użytkownika. W poniższym przykładzie pokazano, jak można ponownie użyć wystąpienia Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider w różnych SqlCommand obiektach dla tego samego użytkownika. Buforowanie klucza szyfrowania kolumny będzie utrzymywane w wielu zapytaniach, zmniejszając liczbę zapytań do magazynu kluczy.

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

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

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

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

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

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

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

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

Włączanie dodatkowej ochrony dla naruszonego programu SQL Server

Domyślnie dostawca danych microsoft .NET dla programu SQL Server opiera się na systemie bazy danych (SQL Server lub Azure SQL Database), aby zapewnić metadane dotyczące kolumn w bazie danych i sposobu ich szyfrowania. Metadane szyfrowania umożliwiają dostawcy danych microsoft .NET dla programu SQL Server szyfrowanie parametrów zapytania i odszyfrowywanie wyników zapytania bez żadnych danych wejściowych z aplikacji, co znacznie zmniejsza liczbę zmian wymaganych w aplikacji. Jeśli jednak proces programu SQL Server zostanie naruszony, a osoba atakująca naruszy metadane, które program SQL Server wyśle do dostawcy danych microsoft .NET dla programu SQL Server, osoba atakująca może ukraść poufne informacje. W tej sekcji opisano interfejsy API, które pomagają zapewnić dodatkowy poziom ochrony przed tym typem ataku, przy cenie obniżonej przejrzystości.

Wymuszanie szyfrowania parametrów

Zanim dostawca danych platformy Microsoft .NET dla serwera SQL wyśle zapytanie sparametryzowane do serwera SQL, serwer SQL (wywołując sys.sp_describe_parameter_encryption) analizuje instrukcję zapytania, aby uzyskać informacje o tym, które parametry w zapytaniu powinny być szyfrowane. Instancja programu SQL Server, która została naruszona, może wprowadzać w błąd Dostawcę Danych Microsoft .NET dla SQL Server poprzez wysyłanie metadanych wskazujących, że parametr nie dotyczy zaszyfrowanej kolumny, mimo że kolumna jest zaszyfrowana w bazie danych. W związku z tym dostawca danych microsoft .NET dla programu SQL Server nie szyfrował wartości parametru i wysyłał go jako zwykły tekst do naruszonego wystąpienia programu SQL Server.

Aby zapobiec takiemu atakowi, aplikacja może ustawić właściwość SqlParameter.ForceColumnEncryption parametru na true. To ustawienie spowoduje, że dostawca danych microsoft .NET dla programu SQL Server zgłosi wyjątek, jeśli metadane otrzymane z serwera wskazują, że parametr nie musi być zaszyfrowany.

Chociaż użycie właściwości SqlParameter.ForceColumnEncryption pomaga zwiększyć bezpieczeństwo, zmniejsza również przejrzystość szyfrowania do aplikacji klienckiej. W przypadku zaktualizowania schematu bazy danych w celu zmiany zestawu zaszyfrowanych kolumn może być również konieczne wprowadzenie zmian w aplikacji.

Poniższy przykładowy kod ilustruje użycie właściwości SqlParameter.ForceColumnEncryption , aby zapobiec wysyłaniu numerów ubezpieczenia społecznego w postaci zwykłego tekstu do bazy danych.

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

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

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

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

Konfigurowanie ścieżek zaufanego klucza głównego kolumny

Metadane szyfrowania zwracane przez SQL Server dla parametrów zapytań odwołujących się do zaszyfrowanych kolumn, a także dla wyników pobieranych z zaszyfrowanych kolumn, obejmują ścieżkę klucza głównego kolumny, która identyfikuje magazyn kluczy oraz lokalizację klucza w magazynie. Jeśli instancja SQL Server zostanie naruszona, może wysłać ścieżkę klucza kierującą dostawcę danych Microsoft .NET dla SQL Server do lokalizacji kontrolowanej przez osobę atakującą. Ten proces może prowadzić do wycieku poświadczeń magazynu kluczy, jeśli magazyn kluczy wymaga uwierzytelnienia aplikacji.

Aby zapobiec takim atakom, aplikacja może określić listę zaufanych ścieżek kluczy dla danego serwera przy użyciu właściwości SqlConnection.ColumnEncryptionTrustedMasterKeyPaths. Jeśli Microsoft .NET Data Provider for SQL Server otrzyma ścieżkę klucza spoza listy zaufanych ścieżek klucza, zgłosi wyjątek.

Mimo że ustawienie zaufanych ścieżek kluczy zwiększa bezpieczeństwo aplikacji, należy zmienić kod lub/i konfigurację aplikacji za każdym razem, gdy zmienisz klucz główny kolumny (za każdym razem, gdy zmienia się ścieżka klucza głównego kolumny).

W poniższym przykładzie pokazano, jak skonfigurować ścieżki zaufanych kluczy głównych kolumn:

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

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

Kopiowanie zaszyfrowanych danych przy użyciu narzędzia SqlBulkCopy

Za pomocą narzędzia SqlBulkCopy można kopiować dane, które są już szyfrowane i przechowywane w jednej tabeli, do innej tabeli bez odszyfrowywania danych. Aby to zrobić:

  • Upewnij się, że konfiguracja szyfrowania tabeli docelowej jest identyczna z konfiguracją tabeli źródłowej. W szczególności obie tabele muszą mieć te same kolumny zaszyfrowane, a kolumny muszą być szyfrowane przy użyciu tych samych typów szyfrowania i tych samych kluczy szyfrowania. Jeśli którakolwiek z kolumn docelowych jest szyfrowana inaczej niż odpowiadająca jej kolumna źródłowa, nie będzie można odszyfrować danych w tabeli docelowej po operacji kopiowania. Dane zostaną uszkodzone.
  • Skonfiguruj zarówno połączenia bazy danych z tabelą źródłową, jak i z tabelą docelową bez włączonej funkcji Always Encrypted.
  • AllowEncryptedValueModifications Ustaw opcję (zobacz SqlBulkCopyOptions).

Uwaga / Notatka

Podczas określania parametru AllowEncryptedValueModificationsnależy zachować ostrożność. To ustawienie może prowadzić do uszkodzenia bazy danych, ponieważ dostawca danych microsoft .NET dla programu SQL Server nie sprawdza, czy dane są rzeczywiście zaszyfrowane lub czy są prawidłowo szyfrowane przy użyciu tego samego typu szyfrowania, algorytmu i klucza co kolumna docelowa.

Oto przykład, który kopiuje dane z jednej tabeli do innej. Przyjmuje się, że kolumny SSN i BirthDate są szyfrowane.

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

Dokumentacja interfejsu API Always Encrypted

Przestrzeń nazw:Microsoft.Data.SqlClient

Zestaw: Microsoft.Data.SqlClient.dll

Name Description
SqlColumnEncryptionCertificateStoreProvider, klasa Dostawca magazynu kluczy dla magazynu certyfikatów systemu Windows.
Klasa SqlColumnEncryptionCngProvider Dostawca magazynu kluczy dla interfejsu MICROSOFT Cryptography API: Next Generation (CNG).
Klasa SqlColumnEncryptionCspProvider Dostawca magazynu kluczy dla dostawców usług kryptograficznych opartych na programie Microsoft CAPI (CSP).
Klasa SqlColumnEncryptionKeyStoreProvider Klasa podstawowa dostawców przechowywania kluczy.
SqlCommandColumnEncryptionSetting, wyliczenie Ustawienia służące do kontrolowania zachowania funkcji Always Encrypted dla poszczególnych zapytań.
SqlConnectionAttestationProtocol, wyliczenie Określa wartość dla protokołu zaświadczeń podczas używania funkcji Always Encrypted z bezpiecznymi enklawami
Wyliczenie SqlConnectionColumnEncryptionSetting Ustawienia umożliwiające szyfrowanie i odszyfrowywanie dla połączenia z bazą danych.
Właściwość SqlConnectionStringBuilder.ColumnEncryptionSetting Pobiera i ustawia funkcję Always Encrypted w parametrach połączenia.
Właściwość SqlConnection.ColumnEncryptionQueryMetadataCacheEnabled Włącza i wyłącza buforowanie metadanych zapytań szyfrowania.
SqlConnection.ColumnEncryptionKeyCacheTtl właściwość Pobiera i ustawia czas wygaśnięcia dla wpisów w pamięci podręcznej klucza szyfrowania kolumny.
SqlConnection.ColumnEncryptionTrustedMasterKeyPaths, właściwość Umożliwia ustawienie listy zaufanych ścieżek kluczy dla serwera bazy danych. Jeśli podczas przetwarzania zapytania aplikacji sterownik otrzymuje ścieżkę klucza, która nie znajduje się na liście, zapytanie zakończy się niepowodzeniem. Ta właściwość zapewnia dodatkową ochronę przed atakami, które dotyczą naruszonego SQL Servera, wykorzystującego fałszywe ścieżki kluczy, co może prowadzić do wycieku poświadczeń repozytorium kluczy.
Metoda SqlConnection.RegisterColumnEncryptionKeyStoreProviders Umożliwia zarejestrowanie niestandardowych dostawców zarządzania kluczami. Jest to słownik mapujący nazwy dostawców przechowywania kluczy na ich implementacje.
Konstruktor SqlCommand (String, SqlConnection, SqlTransaction, SqlCommandColumnEncryptionSetting) Umożliwia kontrolowanie zachowania funkcji Always Encrypted dla poszczególnych zapytań.
Właściwość SqlParameter.ForceColumnEncryption Wymusza szyfrowanie parametru. Jeśli program SQL Server informuje sterownik, że parametr nie musi być zaszyfrowany, zapytanie używające parametru zakończy się niepowodzeniem. Ta właściwość zapewnia dodatkową ochronę przed atakami zabezpieczeń, które obejmują naruszone zabezpieczenia programu SQL Server, zapewniając klientowi nieprawidłowe metadane szyfrowania, co może prowadzić do ujawnienia danych.
ciąg połączenia słowo kluczowe: Column Encryption Setting=enabled Włącza lub wyłącza funkcję Always Encrypted dla połączenia.

Zobacz także