sql >> Database teknologi >  >> RDS >> Sqlserver

SQL Server 2016:Ydeevnepåvirkning af altid krypteret

Som en del af T-SQL Tuesday #69 har jeg blogget om begrænsningerne ved Always Encrypted, og jeg nævnte der, at ydeevnen kan blive negativt påvirket af brugen af ​​det (som du måske forventer, har stærkere sikkerhed ofte afvejninger). I dette indlæg ville jeg tage et hurtigt kig på dette, idet jeg huskede (igen), at disse resultater er baseret på CTP 2.2-kode, så meget tidligt i udviklingscyklussen, og ikke nødvendigvis afspejler den ydeevne, du vil se kom RTM.

Først ville jeg demonstrere, at Always Encrypted virker fra klientapplikationer, selvom den seneste version af SQL Server 2016 ikke er installeret der. Du skal dog installere .NET Framework 4.6 preview (den seneste version her, og det kan ændre sig) for at understøtte Column Encryption Setting forbindelsesstrengattribut. Hvis du kører Windows 10 eller har installeret Visual Studio 2015, er dette trin ikke nødvendigt, da du allerede burde have en ny nok version af .NET Framework.

Dernæst skal du sikre dig, at Always Encrypted-certifikatet findes på alle klienter. Du opretter master- og kolonnekrypteringsnøglerne i databasen, som enhver Always Encrypted tutorial vil vise dig, så skal du eksportere certifikatet fra den maskine og importere det til de andre, hvor applikationskoden vil køre. Åbn certmgr.msc , og udvid Certificates – Current User> Personal> Certificates, og der skulle være et der kaldet Always Encrypted Certificate . Højreklik på det, vælg Alle opgaver> Eksporter, og følg vejledningen. Jeg eksporterede den private nøgle og angav en adgangskode, som producerede en .pfx-fil. Så gentager du bare den modsatte proces på klientmaskinerne:Åbn certmgr.msc , udvid Certifikater – Aktuel bruger> Personlig, højreklik på Certifikater, vælg Alle opgaver> Importer, og peg på den .pfx-fil, du oprettede ovenfor. (Officiel hjælp her.)

(Der er mere sikre måder at administrere disse certifikater på – det er ikke sandsynligt, at du bare vil installere certifikatet på denne måde på alle maskiner, da du snart vil spørge dig selv, hvad var meningen? Jeg gjorde kun dette i mit isolerede miljø i forbindelse med denne demo – jeg ville sikre mig, at min applikation hentede data over ledningen og ikke kun i lokal hukommelse.)

Vi opretter to databaser, en med en krypteret tabel og en uden. Vi gør dette for at isolere forbindelsesstrenge og også for at måle pladsforbrug. Selvfølgelig er der mere detaljerede måder at kontrollere, hvilke kommandoer der skal bruge en krypteringsaktiveret forbindelse - se noten med titlen "Kontrol af ydeevnepåvirkningen..." i denne artikel.

Tabellerne ser således ud:

-- krypteret kopi, i databasen Krypteret CREATE TABLE dbo.Employees( ID INT IDENTITY(1,1) PRIMARY KEY, LastName NVARCHAR(32) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (ENCRYPTION_TYPE =DETERMINISTIC_6CHMACTION_CHA_6KEYN. ColumnKey) IKKE NULL, Løn INT KRYPTET MED (ENCRYPTION_TYPE =RANDOMIZED, ALGORITHM ='AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY =ColumnKey) IKKE NULL); -- ukrypteret kopi, i database Normal CREATE TABLE dbo.Employees( ID INT IDENTITY(1,1) PRIMARY KEY, Efternavn NVARCHAR(32) COLLATE Latin1_General_BIN2 IKKE NULL, Løn INT IKKE NULL);

Med disse tabeller på plads, ønskede jeg at konfigurere et meget simpelt kommandolinjeprogram til at udføre følgende opgaver mod både de krypterede og ukrypterede versioner af tabellen:

  • Indsæt 100.000 medarbejdere, én ad gangen
  • Læs gennem 100 tilfældige rækker, 1.000 gange
  • Output tidsstempler før og efter hvert trin

Så vi har en lagret procedure i en helt separat database, der bruges til at producere tilfældige heltal til at repræsentere lønninger, og tilfældige Unicode-strenge af varierende længde. Vi vil gøre dette en ad gangen for bedre at simulere reel brug af 100.000 inserts, der sker uafhængigt (dog ikke samtidig, da jeg ikke er modig nok til at forsøge at udvikle og administrere en multi-threaded C#-applikation korrekt, eller prøve at koordinere og synkronisere flere forekomster af en enkelt applikation).

CREATE DATABASE Utility;GO USE Utility;GO CREATE PROCEDURE dbo.GenerateNameAndSalary @Name NVARCHAR(32) OUTPUT, @Salary INT OUTPUTASBEGIN SET NOCOUNT ON; SELECT @Name =LEFT(CONVERT(NVARCHAR(32), CRYPT_GEN_RANDOM(64)), RAND() * 32 + 1); VÆLG @Løn =KONVERTER(INT, RAND()*100000)/100*100;ENDGO

Et par rækker af prøveoutput (vi er ligeglade med det faktiske indhold af strengen, bare at det varierer):

酹2“. 

Så vil de lagrede procedurer, som applikationen i sidste ende kalder (disse er identiske i begge databaser, da dine forespørgsler ikke skal ændres for at understøtte Always Encrypted):

OPRET PROCEDURE dbo.AddPerson @Efternavn NVARCHAR(32), @Løn INTASBEGIN SET NOCOUNT ON; INDSÆT dbo.Medarbejdere(Efternavn, Løn) VÆLG @Efternavn, @Løn;ENDGO OPRET PROCEDURE dbo.HentPeopleASBEGIN SÆT INGEN TÆLLING TIL; VÆLG TOP (100) ID, Efternavn, Løn FRA dbo.Medarbejdere BESTIL EFTER NEWID();ENDGO

Nu, C#-koden, startende med connectionStrings-delen af ​​App.config. Den vigtige del er Column Encryption Setting mulighed for kun databasen med de krypterede kolonner (antag for kortheds skyld, at alle tre forbindelsesstrenge indeholder den samme Data Source , og den samme SQL-godkendelse User ID og Password ):

   

Og Program.cs (beklager, for demoer som denne er jeg forfærdelig til at gå ind og omdøbe tingene logisk):

using System;using System.Collections.Generic;using System.Text;using System.Configuration;using System.Data;using System.Data.SqlClient; navneområde AEDemo{ class Program { static void Main(string[] args) { using (SqlConnection con1 =new SqlConnection()) { Console.WriteLine(DateTime.UtcNow.ToString("hh:mm:ss.fffffff")); streng navn; string EmptyString =""; int løn; int i =1; while (i <=100000) { con1.ConnectionString =ConfigurationManager.ConnectionStrings["Utility"].ToString(); ved hjælp af (SqlCommand cmd1 =new SqlCommand("dbo.GenerateNameAndSalary", con1)) { cmd1.CommandType =CommandType.StoredProcedure; SqlParameter n =new SqlParameter("@Name", SqlDbType.NVarChar, 32) { Direction =ParameterDirection.Output }; SqlParameter s =new SqlParameter("@Salary", SqlDbType.Int) { Direction =ParameterDirection.Output }; cmd1.Parameters.Add(n); cmd1.Parameters.Add(s); con1.Åbn(); cmd1.ExecuteNonQuery(); navn =n.Value.ToString(); løn =Convert.ToInt32(s.Value); con1.Luk(); } ved hjælp af (SqlConnection con2 =new SqlConnection()) { con2.ConnectionString =ConfigurationManager.ConnectionStrings[args[0]].ToString(); ved hjælp af (SqlCommand cmd2 =new SqlCommand("dbo.AddPerson", con2)) { cmd2.CommandType =CommandType.StoredProcedure; SqlParameter n =new SqlParameter("@Efternavn", SqlDbType.NVarChar, 32); SqlParameter s =new SqlParameter("@Salary", SqlDbType.Int); n.Værdi =navn; s.Værdi =løn; cmd2.Parameters.Add(n); cmd2.Parameters.Add(s); con2.Åbn(); cmd2.ExecuteNonQuery(); con2.Close(); } } i++; } Console.WriteLine(DateTime.UtcNow.ToString("tt:mm:ss.ffffffff")); i =1; while (i <=1000) { using (SqlConnection con3 =new SqlConnection()) { con3.ConnectionString =ConfigurationManager.ConnectionStrings[args[0]].ToString(); ved hjælp af (SqlCommand cmd3 =new SqlCommand("dbo.RetrievePeople", con3)) { cmd3.CommandType =CommandType.StoredProcedure; con3.Åbn(); SqlDataReader rdr =cmd3.ExecuteReader(); while (rdr.Read()) { EmptyString +=rdr[0].ToString(); } con3.Close(); } } i++; } Console.WriteLine(DateTime.UtcNow.ToString("tt:mm:ss.ffffffff")); } } }}

Så kan vi kalde .exe med følgende kommandolinjer:

AEDemoConsole.exe "Normal" AEDemoConsole.exe "Krypter"

Og det vil producere tre linjers output for hvert opkald:starttidspunktet, tiden efter at 100.000 rækker blev indsat, og tiden efter 100 rækker blev læst 1.000 gange. Her var de resultater, jeg så på mit system, i gennemsnit over 5 løb hver:

Varighed (sekunder) af skrivning og læsning af data

Der er en klar effekt ved at skrive dataene - ikke helt 2X, men mere end 1,5X. Der var et meget lavere delta ved læsning og dekryptering af dataene – i hvert fald i disse tests – men det var heller ikke gratis.

Hvad angår pladsforbrug, er der groft sagt en 3X straf for lagring af krypterede data (i betragtning af arten af ​​de fleste krypteringsalgoritmer burde dette ikke være chokerende). Husk, at dette var på en tabel med kun en enkelt klynget primærnøgle. Her var tallene:

Mellemrum (MB) brugt til at gemme data

Så der er selvfølgelig nogle bøder ved at bruge Always Encrypted, som der typisk er med stort set alle sikkerhedsrelaterede løsninger (ordsproget "ingen gratis frokost" kommer til at tænke på). Jeg vil gentage, at disse tests blev udført mod CTP 2.2, som kan være radikalt anderledes end den endelige udgivelse af SQL Server 2016. Disse forskelle, som jeg har observeret, kan også kun afspejle arten af ​​de test, jeg lavede; Jeg håber selvfølgelig, at du kan bruge denne tilgang til at teste dine resultater mod dit skema, på din hardware og med dine dataadgangsmønstre.


  1. TIMEDIFF() vs SUBTIME() i MySQL:Hvad er forskellen?

  2. MySQL COUNT() – Få antallet af rækker, der skal returneres af en forespørgsel

  3. oracle - konverter mange datoformater til en enkelt formateret dato

  4. Fremmednøgle til ikke-primær nøgle