• Привет, Гость!
  • Войти
  • Регистрация
  • Записи
  • Форумы
  • Люди
  • Файлы
  • Работа
  • Технологии
  • Все
  • Новости
  • События
  • Статьи
  • Блоги

MS SQL 2008 : Прозрачное шифрование баз данных (TDE) в разрезе

MS SQL 2008 : Прозрачное шифрование баз данных (TDE) в разрезе

yliberman
27.05.2008 6:26

Введение

В этом посте описываются тонкости реализации TDE в Microsoft SQL Server 2008, а также предлагается основанная на этих знаниях методика прогнозирования влияния TDE на производительность приложения. Решение, построенное на этой методике, было описано в первой части.

В качестве основы давайте возьмем результаты раздела "Оценка дополнительных затрат ресурсов на шифрование в TDE" из моего поста "SQL Internals Profiler. Часть 2 (примеры использования)". Там мы выполняли вставку данных в таблицу, которая была создана в зашифрованной базе данных, и с помощью SQLInternalsProfiler определили, какие именно криптографические операции выполнял SQL сервер и сколько времени процессора это заняло.

-- Основной цикл теста - вставляем много строк в таблицу
DECLARE @i int = 0
WHILE(@i < 100000) BEGIN
    INSERT dbo.TestTable(Id, Data) VALUES(@i, REPLICATE ('a', 8000))
    SET @i += 1
END

Результат который мы там получили:

Эти результаты не совсем укладываются в декларируемую логику работы TDE - "при сохранении на диск данные шифруются, а при чтении с диска расшифровываются". В частности мы видим, что очень много времени процессора ушло на расшифровку данных, хотя, если следовать логике TDE, то вообще ничего не должно было расшифровываться (мы же только пишем данные). Также из результата видно, что значительную нагрузку на процессор создала операция восстановления ключа (CryptImportKey).

Сбор данных

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

SET NOCOUNT ON
-- Очищаем кэш
CHECKPOINT
DBCC DROPCLEANBUFFERS
-- Настраиваем перехват криптографических функций
EXECUTE master..xp_SQLInternalsProfiler_AddSource_IAT 'ADVAPI32.dll', 'CryptEncrypt', 'n7dgp6opb6', 1
EXECUTE master..xp_SQLInternalsProfiler_AddSource_IAT 'ADVAPI32.dll', 'CryptDecrypt', 'dgp6opb6', 1
EXECUTE master..xp_SQLInternalsProfiler_AddSource_IAT 'ADVAPI32.dll', 'CryptImportKey', 'dg3ob3', 1
EXECUTE master..xp_SQLInternalsProfiler_AddSource_IAT 'ADVAPI32.dll', 'CryptAcquireContextW', '.', 1
-- Создаем таблицу, куда будем вставлять данные
IF(OBJECT_ID('dbo.TestTable1') is not NULL) DROP TABLE dbo.TestTable1
CREATE TABLE dbo.TestTable1
(
    Id int primary key,
    Data varchar(8000) not null
)
-- Вставляем в таблицу много строк
DECLARE @i int
SET @i = 0
BEGIN TRAN
WHILE(@i < 50000) BEGIN
    INSERT dbo.TestTable1(Id, Data) VALUES(@i, REPLICATE('a', 8000))
    SET @i = @i + 1
END
COMMIT
CHECKPOINT
-- Создаем таблицу #Result и записываем в нее результат
CREATE TABLE #Results
(
    source_name varchar(128) not null,
    block_size int null,
    [count] bigint not null,
    second_time decimal(12, 6) not null,
    percent_time decimal(8, 2) not null,
    bytes_count bigint not null
)
INSERT #Results
EXECUTE master..xp_SQLInternalsProfiler_Results 1925
EXECUTE master..xp_SQLInternalsProfiler_StopAll 1

Результатом выполнения выше приведенного запроса является таблица #Results. Информация, собранная в ней, используется ниже для дальнейшего анализа.

Файлы данных

Для того, чтобы отобрать криптографические операции, которые относятся к файлам данных, запросим из таблицы #Results все операции с длиной блока данных равной 8096 байт (длина страницы 8192 байта минус длина заголовка страницы, который не шифруется, 96 байт) и мало-мальски значимым временем выполнения (не менее одного процента от всего времени выполнения запроса).

-- MDF
SELECT * FROM #Results WHERE block_size = 8096 And percent_time >= 1

С первой строкой все понятно. Она говорит о том, что немногим более 50000 страниц было зашифровано. Именно такое количество строк мы и вставляли. Каждая строка у нас занимала отдельную страницу.

Со второй строкой не все ясно. Откуда вообще взялась расшифровка, да еще в таких количествах?! Как видно из таблицы, расшифровано было даже больше данных, чем зашифровано. И это при том, что мы вообще ничего не читали (только писали). Неявные чтения различного рода служебной информации такого объема расшифровок, конечно, не объясняют. Дело в том, что страница перед записью на диск шифруется непосредственно в кэше страниц (BPool). Очевидно, что в памяти в таком виде она абсолютно бесполезна. Поэтому, после того как операция записи страницы на диск завершается, в памяти страница расшифровывается обратно. Таким образом получается, что запись страницы в файл данных порождает выполнение сразу двух криптографических операций - шифрования и расшифровки.

Журнал транзакций

Для отображения криптографических операции, относящихся к журналу транзакций, запросим из таблицы #Results все операции с длиной блока данных большей 8096 байт (мы вставляли строки в таблицу в рамках одной большой транзакции, так что можно ожидать, что SQL сервер будет писать в журнал транзакций относительно большими кусками).

-- LDF
SELECT * FROM #Results WHERE block_size > 8096 And percent_time >= 1

Как видим, тут обошлось без сюрпризов. Наши ожидания подтвердились. SQL север действительно сгруппировал информацию о нескольких INSERT'ах в блоки данных размером немногим менее 64 КБ. Затем каждый блок данных зашифровал и записал в журнал транзакций.

Другие операции (восстановление DEK)

Выше мы разобрались с операциями с длиной блока данных равной и превышающей 8096 байт. Что же осталось?

SELECT * FROM #Results WHERE block_size < 8096 And percent_time >= 1

А осталось довольно много. Какую задачу выполняют эти операции?

Первые две - это восстановление ключа шифрования базы данных (DEK). Каждый раз когда SQL сервер открывает (создает) файл данных или виртуальный журнал транзакций для зашифрованной базы данных, SQL сервер восстанавливает (расшифровывает) для него новый экземпляр DEK. Подчеркну, что в случае журналов транзакций DEK восстанавливается (расшифровывается) для каждого виртуального журнала транзакций в отдельности, а не для всего файла журнала в целом! Объем записи в журнал транзакций может быть довольно большим (как в нашем примере), и тогда SQL сервер вынужден создавать много новых виртуальных журналов транзакций. Например, за время выполнения нашего тестового запроса SQL сервер создал 359 новых виртуальных журналов транзакций. Я получил эту информацию с помощью команды DBCC LOGINFO. Для каждого нового виртуального журнала SQL сервер восстановил (расшифровал) DEK, и это породило в нашем случае 359 вызовов функций CryptDecrypt и CryptImportKey.

После того, как SQL сервер восстанавливает DEK, он снимает с него 50 независимых копий для того, чтобы каждый шедулер мог использовать свою копию ключа не мешая друг другу. Даже если в системе нет столько шедулеров все-равно создается 50 экземпляров DEK. Каждая копия создается в отдельном контексте. Итого, на каждый DEK требуется 51 контекст. Так в нашем примере образовалось 18309 (= 359 * 51) вызовов функции CryptAcquireContext.

В SQL 2008 для защиты DEK используется ассиметричное шифрование (DEK шифруется сертификатом из базы данных master). Как следствие, восстановление DEK - достаточно долгая и ресурсоемкая операция. Именно поэтому даже всего 359 таких операций (как в нашем примере) создали ощутимую дополнительную нагрузку. В примере я использовал сертификат с длиной ключа 1024 бит (значение по умолчанию). Я пробовал использовать большие длины ключа, например 2048 бит и это приводило к тому, что затраты на восстановление DEK иногда перекрывали затраты на все остальное.

Методика прогнозирования дополнительной нагрузки создаваемой TDE

Основываясь на выше изложенной информации, можно прогнозировать объем дополнительной нагрузки создаваемой TDE для существующих приложений. Исходная информация, которая нам нужна:

Величина Единица измерения Комментарий
Характеристики сервера
CPU_enabled штук Количество установленных в сервере процессоров (ядер), на которое будет ориентироваться прогноз.
V_sym_crypt МБ / сек. Скорость шифрования симметричным алгоритмом, который планируется использовать для шифрования данных.
V_sym_decrypt МБ / сек. Скорость расшифровки.
V_asym_decrypt операций / сек. Скорость расшифровки ключа (DEK) зашифрованного ассиметричным алгоритмом.
V_acquire_context операций / сек. Скорость выполнения CryptoAPI функции CryptAcquireContext.
Характеристики нагрузки
T_interval сек. Длительность интервала измерения.
Z_interval сек. cpu / сек. Средняя загрузка процессора в течении интервала измерения (каждый процессор / ядро считается отдельно).
IO_data_read МБ / сек. Средний объем чтения из файлов данных в течении интервала измерения.
IO_data_write МБ / сек. Средний объем записи в файлы данных в течении интервала измерения.
IO_log_read МБ / сек. Средний объем чтения из журналов транзакций в течении интервала измерения.
IO_log_write МБ / сек. Средний объем записи в журналы транзакций в течении интервала измерения.
IO_log_vfile штук Количество виртуальных журналов транзакций, которые были созданы в течении интервала измерения.

1. Определяем общее потребление процессора за весь интервал измерения:

С_interval = T_interval * Z_interval

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

C_sym_encrypt = T_interval * (IO_log_write + IO_data_write) / V_sym_crypt
C_sym_decrypt = T_interval * (IO_log_read + IO_data_read + IO_data_write) / V_sym_decrypt
C_asym_decrypt = IO_log_vfile / V_asym_decrypt
C_acquire_context = 51 * IO_log_vfile / V_acquire_context
C_TDE = C_sym_encrypt + C_sym_decrypt + C_asym_decrypt + C_acquire_context

3. Определяем общее потребление CPU за интервал, после включения TDE

TDE(C_interval) = C_interval + C_TDE

4. Теперь определим границы, в рамках которых скорее всего будет находится новая длительность интервала после включения TDE.

4.1 Очевидно, что после включения TDE запросы будут выполняться как минимум не быстрее, то есть прогнозируемое значение не может быть меньше T_interval. Кроме того за прогнозируемый интервал с учетом имеющихся процессоров (ядер) должна успеть выполнится вся прогнозируемая нагрузка. Получаем формулу:

TDE(T_interval) min = MAX(T_interval, TDE(C_interval) / CPU_enabled)

4.2 С другой стороны время выполнения запросов не должно увеличиться больше чем на время нужное для выполнения всей дополнительной работы:

TDE(T_interval) max = T_interval + C_TDE / CPU_enabled

5. Все остальные значения легко получаются из уже посчитанных:

TDE(Z_interval) = TDE(C_interval) / TDE(T_interval)
TDE(IO_data_read) = IO_data_read * T_interval / TDE(T_interval)
TDE(IO_data_write) = IO_data_write * T_interval / TDE(T_interval)
TDE(IO_log_read) = IO_log_read * T_interval / TDE(T_interval)
TDE(IO_log_write) = IO_log_write * T_interval / TDE(T_interval)

Все.

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

Заключение

В заключение хочу отметить, что некоторые технические решения, использованные в TDE, представляются мне не самыми удачными. Например, восстановление DEK для каждого нового виртуального журнала транзакций вместо того, чтобы взять у предыдущего (уже заполненного) виртуального журнала. Я надеюсь, что так сделали не просто так, а потому что на это были веские причины, неизвестные мне ;)

* Использовался MS SQL 2008 CTP6

Ссылки

MSDN: Database Encryption in SQL Server 2008 Enterprise Edition

<script src="http://www.google-analytics.com/urchin.js" type=text/javascript> </script> <script type=text/javascript> _uacct = "UA-2408004-1"; urchinTracker(); </script>
yliberman
27.05.2008 6:26
Комментариев:0 RSS Просмотров:382
Теги: Security, SQL, SQL 2008

Yan Liberman

yliberman разработчик
(none)
  • Блог

Облако тегов

advertisement bug scripting security sql sql 2008 sql ug
Строишь сложные системы? Хостинг от Parking.Ru

Записи

Популярные
  • k0stya > Система контроля версий для базы данных
  • Oxozle > Руководство по отладке многопоточных приложений в Visual Studio 2010
  • Jeje > Тюнинг производительно­сти для ASP.NET. Часть 1
  • Jeje > Razor - новый движок представлений в ASP.NET
  • Oxozle > Профилирование приложений в Visual Studio 2010
  • Jeje > NerdDinner. Шаг 1: Новый проект
  • Oxozle > Visual Studio 2010 Productivity Power Tools
  • sergeypopov > Ссылки к докладу «Расширяем Visual Studio 2010»
  • Jeje > NerdDinner. Шаг 2: Создание базы данных
  • mezastel > Паттерн-мэтчинг на языке C#
Все популярные записи
Обсуждаемые
  • k0stya > Система контроля версий для базы данных
  • Oxozle > Руководство по отладке многопоточных приложений в Visual Studio 2010
  • XaocCPS > Срочно! Вышел Razor View Engine и открыто имя нового проекта WebMatrix
  • mvcdev > Локализация ASP.NET приложений. Стиль кодирования
  • XaocCPS > Pivot-коллекция (silverlight) для навигации по статьям журнала MSDN Magazine
  • Jeje > NerdDinner. Шаг 2: Создание базы данных
  • Sharomank > Расширение Regex Tester для Visual Studio 2010
  • XaocCPS > Анонсирован SQL Server Compact Edition 4
  • Oxozle > Visual Studio 2010 Productivity Power Tools
  • XaocCPS > Выпущена библиотека ADO.NET Entity Framework Feature CTP 4
Все обсуждаемые записи

Блоги

Новые
  • Stanislav Gornakov [MVP]> Stanislav Gornakov
  • k0stya> k0stya
  • ][tiger> Just do IT - просто дует
  • Oxozle> KLUBS
  • mvcdev> WebDev
  • VitaliyP> PanarinV
  • Tamifist> Tamifist
  • kir> KLypkan
  • sadomovalex> Alexey Sadomov
  • noetic> Систематизация автоматизации
Обсуждаемые
  • mihailik> Олег Михайлик
  • ceo> Нотатник Вiктора Шатохiна [MSFT]
  • gaidar> Gaidar Magdanurov
  • MikhailChernomo­rdikov> Mikhail Chernomordikov [MSFT]
  • Alexander Lozhechkin [MSFT]> Alexander Lozhechkin
  • agladkik> Andrey Gladkikh: Microsoft Dynamics
  • sergun> Sergey Zwezdin
  • beerbong> Bong Blog
  • sos> Dmitry Soshnikov [MSFT]
  • not-a-kernel-gu­y> Зеркало: Not a kernel guy
О сайте   Свяжитесь с нами   Версия для печати
Работает на 1С-Битрикс: Управление сайтом ASP.NET  |  Хостинг на Parking.Ru