टी-एसक्यूएल मंगलवार #69 के हिस्से के रूप में, मैंने हमेशा एन्क्रिप्टेड की सीमाओं के बारे में ब्लॉग किया है, और मैंने वहां उल्लेख किया है कि इसके उपयोग से प्रदर्शन नकारात्मक रूप से प्रभावित हो सकता है (जैसा कि आप उम्मीद कर सकते हैं, मजबूत सुरक्षा में अक्सर ट्रेड-ऑफ होता है)। इस पोस्ट में, मैं (फिर से) ध्यान में रखते हुए इस पर एक त्वरित नज़र डालना चाहता था कि ये परिणाम सीटीपी 2.2 कोड पर आधारित हैं, इसलिए विकास चक्र में बहुत जल्दी, और जरूरी नहीं कि आप प्रदर्शन के प्रतिबिंबित हों। देखें आरटीएम आएं।
सबसे पहले, मैं यह प्रदर्शित करना चाहता था कि हमेशा एन्क्रिप्टेड क्लाइंट अनुप्रयोगों से काम करता है, भले ही SQL सर्वर 2016 का नवीनतम संस्करण वहां स्थापित न हो। हालांकि, आपको Column Encryption Setting
का समर्थन करने के लिए .NET Framework 4.6 पूर्वावलोकन (यहां नवीनतम संस्करण, और जो बदल सकता है) को स्थापित करना होगा। कनेक्शन स्ट्रिंग विशेषता। यदि आप Windows 10 चला रहे हैं, या आपने Visual Studio 2015 स्थापित किया है, तो यह चरण आवश्यक नहीं है, क्योंकि आपके पास पहले से ही .NET Framework का पर्याप्त संस्करण होना चाहिए।
इसके बाद, आपको यह सुनिश्चित करना होगा कि सभी क्लाइंट पर हमेशा एन्क्रिप्टेड प्रमाणपत्र मौजूद है। आप डेटाबेस के भीतर मास्टर और कॉलम एन्क्रिप्शन कुंजी बनाते हैं, जैसा कि कोई भी हमेशा एन्क्रिप्टेड ट्यूटोरियल आपको दिखाएगा, फिर आपको उस मशीन से प्रमाण पत्र निर्यात करना होगा, और इसे अन्य पर आयात करना होगा जहां एप्लिकेशन कोड चलेगा। ओपन certmgr.msc
, और प्रमाणपत्रों का विस्तार करें - वर्तमान उपयोगकर्ता> व्यक्तिगत> प्रमाणपत्र, और वहां एक होना चाहिए जिसे Always Encrypted Certificate
कहा जाता है . उस पर राइट-क्लिक करें, सभी कार्य> निर्यात चुनें, और संकेतों का पालन करें। मैंने निजी कुंजी निर्यात की और एक पासवर्ड प्रदान किया, जिसने एक .pfx फ़ाइल तैयार की। फिर आप क्लाइंट मशीनों पर विपरीत प्रक्रिया को दोहराते हैं:ओपन certmgr.msc
, प्रमाणपत्र विस्तृत करें - वर्तमान उपयोगकर्ता> व्यक्तिगत, प्रमाणपत्रों पर राइट-क्लिक करें, सभी कार्य> आयात चुनें, और इसे ऊपर बनाई गई .pfx फ़ाइल पर इंगित करें। (आधिकारिक सहायता यहां।)
(इन प्रमाणपत्रों को प्रबंधित करने के अधिक सुरक्षित तरीके हैं - यह संभव नहीं है कि आप इस तरह के प्रमाणपत्र को सभी मशीनों पर तैनात करना चाहते हैं, क्योंकि आप जल्द ही खुद से पूछेंगे कि क्या बात थी? मैं केवल अपने अलग वातावरण में ऐसा कर रहा था। इस डेमो के प्रयोजनों के लिए - मैं यह सुनिश्चित करना चाहता था कि मेरा एप्लिकेशन वायर पर डेटा पुनर्प्राप्त कर रहा था, न कि केवल स्थानीय मेमोरी में।)
हम दो डेटाबेस बनाते हैं, एक एन्क्रिप्टेड टेबल के साथ, और एक बिना। हम ऐसा कनेक्शन स्ट्रिंग्स को अलग करने और अंतरिक्ष उपयोग को मापने के लिए भी करते हैं। बेशक, एन्क्रिप्शन-सक्षम कनेक्शन का उपयोग करने के लिए किन आदेशों को नियंत्रित करने के अधिक बारीक तरीके हैं - इस लेख में "प्रदर्शन प्रभाव को नियंत्रित करना ..." शीर्षक वाला नोट देखें।
टेबल इस तरह दिखती हैं:
-- encrypted copy, in database Encrypted CREATE TABLE dbo.Employees ( ID INT IDENTITY(1,1) PRIMARY KEY, LastName NVARCHAR(32) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = ColumnKey) NOT NULL, Salary INT ENCRYPTED WITH (ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY = ColumnKey) NOT NULL ); -- unencrypted copy, in database Normal CREATE TABLE dbo.Employees ( ID INT IDENTITY(1,1) PRIMARY KEY, LastName NVARCHAR(32) COLLATE Latin1_General_BIN2 NOT NULL, Salary INT NOT NULL );
इन तालिकाओं के साथ, मैं तालिका के एन्क्रिप्टेड और अनएन्क्रिप्टेड दोनों संस्करणों के विरुद्ध निम्नलिखित कार्यों को करने के लिए एक बहुत ही सरल कमांड-लाइन एप्लिकेशन सेट करना चाहता था:
- एक बार में एक लाख कर्मचारियों को सम्मिलित करें
- 100 यादृच्छिक पंक्तियों के माध्यम से, 1,000 बार पढ़ें
- प्रत्येक चरण के पहले और बाद में आउटपुट टाइमस्टैम्प
इसलिए हमारे पास एक पूरी तरह से अलग डेटाबेस में एक संग्रहीत प्रक्रिया है जिसका उपयोग वेतन का प्रतिनिधित्व करने के लिए यादृच्छिक पूर्णांक और अलग-अलग लंबाई के यादृच्छिक यूनिकोड स्ट्रिंग्स का उत्पादन करने के लिए किया जाता है। हम स्वतंत्र रूप से होने वाले 100,000 आवेषणों के वास्तविक उपयोग को बेहतर ढंग से अनुकरण करने के लिए एक समय में ऐसा करने जा रहे हैं (हालांकि समवर्ती रूप से नहीं, क्योंकि मैं एक बहु-थ्रेडेड सी # एप्लिकेशन को ठीक से विकसित और प्रबंधित करने का प्रयास करने के लिए पर्याप्त बहादुर नहीं हूं, या समन्वय करने का प्रयास करता हूं और एक ही एप्लिकेशन के कई इंस्टेंस को सिंक्रोनाइज़ करें)।
CREATE DATABASE Utility; GO USE Utility; GO CREATE PROCEDURE dbo.GenerateNameAndSalary @Name NVARCHAR(32) OUTPUT, @Salary INT OUTPUT AS BEGIN SET NOCOUNT ON; SELECT @Name = LEFT(CONVERT(NVARCHAR(32), CRYPT_GEN_RANDOM(64)), RAND() * 32 + 1); SELECT @Salary = CONVERT(INT, RAND()*100000)/100*100; END GO
नमूना आउटपुट की कुछ पंक्तियाँ (हमें स्ट्रिंग की वास्तविक सामग्री की परवाह नहीं है, बस यह भिन्न होती है):
酹2ዌ륒㦢㮧羮怰㉤盿⚉嗝䬴敏⽁캘♜鼹䓧 98600 贓峂쌄탠❼缉腱蛽☎뱶 72000
फिर संग्रहीत कार्यविधियों को एप्लिकेशन अंततः कॉल करेगा (ये दोनों डेटाबेस में समान हैं, क्योंकि आपके प्रश्नों को हमेशा एन्क्रिप्टेड का समर्थन करने के लिए बदलने की आवश्यकता नहीं है):
CREATE PROCEDURE dbo.AddPerson @LastName NVARCHAR(32), @Salary INT AS BEGIN SET NOCOUNT ON; INSERT dbo.Employees(LastName, Salary) SELECT @LastName, @Salary; END GO CREATE PROCEDURE dbo.RetrievePeople AS BEGIN SET NOCOUNT ON; SELECT TOP (100) ID, LastName, Salary FROM dbo.Employees ORDER BY NEWID(); END GO
अब, C# कोड, App.config के कनेक्शनस्ट्रिंग भाग से शुरू होता है। महत्वपूर्ण हिस्सा है Column Encryption Setting
केवल एन्क्रिप्टेड कॉलम वाले डेटाबेस के लिए विकल्प (संक्षिप्तता के लिए, मान लें कि सभी तीन कनेक्शन स्ट्रिंग्स में समान Data Source
है। , और वही SQL प्रमाणीकरण User ID
और Password
):
<connectionStrings> <add name="Utility" connectionString="Initial Catalog=Utility;..."/> <add name="Normal" connectionString="Initial Catalog=Normal;..."/> <add name="Encrypt" connectionString="Initial Catalog=Encrypted; Column Encryption Setting=Enabled;..."/> </connectionStrings>
और Program.cs (क्षमा करें, इस तरह के डेमो के लिए, मैं अंदर जाने और तार्किक रूप से नाम बदलने में भयानक हूं):
using System; using System.Collections.Generic; using System.Text; using System.Configuration; using System.Data; using System.Data.SqlClient; namespace AEDemo { class Program { static void Main(string[] args) { using (SqlConnection con1 = new SqlConnection()) { Console.WriteLine(DateTime.UtcNow.ToString("hh:mm:ss.fffffff")); string name; string EmptyString = ""; int salary; int i = 1; while (i <= 100000) { con1.ConnectionString = ConfigurationManager.ConnectionStrings["Utility"].ToString(); using (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.Open(); cmd1.ExecuteNonQuery(); name = n.Value.ToString(); salary = Convert.ToInt32(s.Value); con1.Close(); } using (SqlConnection con2 = new SqlConnection()) { con2.ConnectionString = ConfigurationManager.ConnectionStrings[args[0]].ToString(); using (SqlCommand cmd2 = new SqlCommand("dbo.AddPerson", con2)) { cmd2.CommandType = CommandType.StoredProcedure; SqlParameter n = new SqlParameter("@LastName", SqlDbType.NVarChar, 32); SqlParameter s = new SqlParameter("@Salary", SqlDbType.Int); n.Value = name; s.Value = salary; cmd2.Parameters.Add(n); cmd2.Parameters.Add(s); con2.Open(); cmd2.ExecuteNonQuery(); con2.Close(); } } i++; } Console.WriteLine(DateTime.UtcNow.ToString("hh:mm:ss.fffffff")); i = 1; while (i <= 1000) { using (SqlConnection con3 = new SqlConnection()) { con3.ConnectionString = ConfigurationManager.ConnectionStrings[args[0]].ToString(); using (SqlCommand cmd3 = new SqlCommand("dbo.RetrievePeople", con3)) { cmd3.CommandType = CommandType.StoredProcedure; con3.Open(); SqlDataReader rdr = cmd3.ExecuteReader(); while (rdr.Read()) { EmptyString += rdr[0].ToString(); } con3.Close(); } } i++; } Console.WriteLine(DateTime.UtcNow.ToString("hh:mm:ss.fffffff")); } } } }
तब हम .exe को निम्न कमांड लाइन के साथ कॉल कर सकते हैं:
AEDemoConsole.exe "Normal" AEDemoConsole.exe "Encrypt"
और यह प्रत्येक कॉल के लिए आउटपुट की तीन पंक्तियों का उत्पादन करेगा:प्रारंभ समय, 100,000 पंक्तियों को सम्मिलित करने के बाद का समय, और 100 पंक्तियों के बाद का समय 1,000 बार पढ़ा गया। मेरे सिस्टम पर मैंने जो परिणाम देखे, उनमें से प्रत्येक का औसत 5 रन से अधिक था:
डेटा लिखने और पढ़ने की अवधि (सेकंड)
डेटा लिखने का स्पष्ट प्रभाव है - 2X नहीं, बल्कि 1.5X से अधिक। डेटा को पढ़ने और डिक्रिप्ट करने पर बहुत कम डेल्टा था - कम से कम इन परीक्षणों में - लेकिन वह भी मुफ़्त नहीं था।
जहां तक अंतरिक्ष उपयोग की बात है, एन्क्रिप्टेड डेटा को संग्रहीत करने के लिए लगभग 3X जुर्माना है (अधिकांश एन्क्रिप्शन एल्गोरिदम की प्रकृति को देखते हुए, यह चौंकाने वाला नहीं होना चाहिए)। ध्यान रखें कि यह केवल एक संकुल प्राथमिक कुंजी वाली तालिका पर था। ये रहे आंकड़े:
डेटा स्टोर करने के लिए इस्तेमाल किया जाने वाला स्पेस (एमबी)
तो स्पष्ट रूप से हमेशा एन्क्रिप्टेड का उपयोग करने के साथ कुछ दंड हैं, क्योंकि आम तौर पर लगभग सभी सुरक्षा-संबंधित समाधान होते हैं (कहना "कोई निःशुल्क दोपहर का भोजन नहीं" दिमाग में आता है)। मैं दोहराता हूँ कि ये परीक्षण CTP 2.2 के विरुद्ध किए गए थे, जो SQL सर्वर 2016 की अंतिम रिलीज़ से मौलिक रूप से भिन्न हो सकते हैं। साथ ही, मैंने जो अंतर देखे हैं, वे केवल मेरे द्वारा बनाए गए परीक्षणों की प्रकृति को दर्शा सकते हैं; स्पष्ट रूप से मुझे आशा है कि आप इस दृष्टिकोण का उपयोग अपने स्कीमा के विरुद्ध, अपने हार्डवेयर पर, और अपने डेटा एक्सेस पैटर्न के साथ अपने परिणामों का परीक्षण करने के लिए कर सकते हैं।