[cz. I] Always Encrypted – podstawy, podstaw… – SQL Server Security

Always Encrypted nowa funkcja dostarczona z SQL Server 2016, szyfruję dane w zdefiniowanych kolumnach. W odróżnieniu od TDE lub Cell Level Encryption te rozwiązanie kompleksowe, ponieważ szyfruję dane w spoczynku, w pamięci oraz podczas transmisji. Szyfrowanie i odszyfrowanie następuję dopiero po stronie klienta, w aplikacji końcowej. Z względu, na specyfikę działania Always Encrypted wymaga zastosowania odpowiedniego sterownika po stronie aplikacji klienckiej.

Always Encrypted

Wspierane sterowniki?

W tej chwili sterowniki wspierające Always Encryption:

Używając jednego z ww. sterowników, aplikacja bezpiecznie przenosi zaszyfrowane dane do bazy danych, które będzie można odszyfrowana tylko przez aplikację, która ma dostęp do klucz. Co prawda każda inna aplikacja może pobrać dane. Jednak dla aplikacji bez dostępu do klucza takie dane będą bezużyteczne, aplikacji zostaną zwrócone szyfrogramy.

Ale jakie klucze?

Zacznijmy od tego, że instancja SQL Server nie musi i nie powinna mieć dostępu do klucza umożliwiającego odszyfrowywanie danych. Jest on poza instancją, inaczej niż w alternatywnych sposobach szyfrowania gdzie wszystkie klucze i certyfikaty znajdują się w obrębie instancji. W tym przypadku jest wg. mnie znacznie prościej, rozróżniamy tylko dwa klucze:

  • Column Encryption Key – szyfruję dane w kolumnach i jest szyfrowany przez Column Master Key. Klucz używa algorytmu AES-256, co potwierdza słuszność szyfrowania kluczem symetrycznym, o czym pisałem tutaj. Jednym kluczem możemy szyfrować dane w różnych kolumnach.
  • Column Master Key – jest kluczem chroniącym klucze służące do szyfrowania Column Encryption Key. Klucz ten może być przechowywany w Windows Certificate Store, Azure Key Vault lub w HSM (hardware security module). SQL Server przechowuję tylko metadane takie jak typ klucza oraz lokalizacja. To ten klucz musi posiadać aplikacja kliencka, aby móc odszyfrować Column Encryption Key, który następnie rozszyfruję dane.

Typy szyfrowania dla kolumn Always Encrypted

Always Encrypted wspiera dwa typy szyfrowania:

  • Deterministyczne – generuję za każdym razem identyczny szyfrogram dla zwykłego tekstu. Dla jednej konkretnej wartości zawszę bedzie identyczna wartość szyfrująca. Wiąże się to z pewnym ryzykiem odgadnięcia rzeczywistej wartości przy małym zbiorze wartości w kolumnie.  Pozwala na wyszukiwanie, łączenie punktowe/równościowe, grupowanie oraz zakładanie indeksu na tej kolumnie. Jednak o zapytaniach z LIKE lub z warunkiem mniejszy, większy można zapomnieć.
  • Losowe – szyfrowanie nie jest przewidywalne, za każdym razem generowana jest niepowtarzalna wartość dla jawnej wartości.  Bezpieczniejsze, lecz uniemożliwia wyszukiwanie równościowe, grupowanie, zakładanie indeksu na tej kolumnie.

Jaki wybrać typ szyfrowania?

Standardowa odpowiedz, „to zależy”. Jeśli planuje się przeszukiwanie oparte na szyfrowanych kolumnach to musimy użyć typu deterministycznego, jednak ciągle pamiętając o ograniczeniach. Jeśli kluczowe jest jednak bezpieczeństwo wybierzemy typ losowy, Jednak trzeba mieć na uwadze, że Always Encrypted nie jest obsługiwane dla kolumn chociażby takiej charakterystyce:

  • Kolumny używające następujących typów danych: xmltimestamp/rowversionimagentexttextsql_varianthierarchyidgeographygeometry, alias, user defined-types.
  • Kolumn FILESTREAM
  • Kolumn z właściwością IDENTITY
  • Kolumn z właściwością ROWGUIDCOL
  • więcej tutaj

A co z indeksami?

O indeksach trochę było omawiając typy szyfrowania kolumn, ale myślę, że nie zaszkodzi powtórzenie pewnych informacji. Kolumny typu deterministycznego mogą zostać użyte, jako klucze indeksów. Dla losowego szyfrowania próba stworzenia takiego indeksu kończy się komunikatem błędu.

Jednak kolumny dla oby dwóch typów szyfrowania mogą zostać użyte, jako kolumny dołączone do indeksów nieklastrowych, aby zapobiegać niepożądanym Lookupom.

Co z wydajnością?

Tabele z kolumnami, które są chronione zajmują znacznie więcej przestrzeni. Oczywiście zależy to od ilości oraz od wielkości kolumny, które chcemy chronić. Dla mojego przykładowej tabeli jest to prawie 50%.

Table Name Row Counts Tota MB %
Users_AE_Deterministic 1822317 139.140625 47.20
Users_AE_Randomized 1822317 139.140625 47.20
Users_NoAE 1822317 94.523437 0

Jak można się domyśleć, jeśli tabela zwiększa swój rozmiar, a procesor bierze udział w procesie szyfrowania i deszyfrowania musi odbić się to na wydajności zapytań. Poniżej wyniki dla bardzo prostych zapytań.

Table Name Time [ms] CPU time [ms] Logical reads
Users_NoAE 187 156 12057
Users_AE_Deterministic 1295 312 109402
Users_AE_Randomized 1566 344 109525

Różnica jest kolosalna, a zapewne zauważyliście, że zapytania do tabel z szyfrowanymi kolumnami, nie sortuję po kolumnie DisplayName. Jest to ograniczenia Always Encrypted. W praktyce takie sortowanie musiałby zostać wykonane przez aplikacje kliencką dopiero po pobraniu danych. Przekłada się to z kolei ponownie na czas, zasoby procesora i pamięć, lecz już na stacji klienckiej.

Podsumowanie

To pierwszy wpis przedstawiająca Always Encrypted. W kolejnym  przedstawie przykład wraz z podsumowaniem wad i zalet Always Encrypted.

Z pasją poświęcam czas na zdobywanie wiedzy w zakresie szeroko rozumianej Data Platform. Zachwycony językiem skryptowym Windows PowerShell. Swoją wiedzę, doświadczenia i spostrzeżenia opisuję na blogu.

Leave a Reply

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *