Шифрование трафика между SQL Server и клиентом. Часть 1. Постановка задачи.
В отличие от возможности прозрачного шифрования базы, появившейся в SQL Server 2008 (Enterprise Ed.), шифрование соединения между сервером и клиентом известно еще со времен SQL Server 7.0. Следует отметить, что одно не отменяет другое. Это очевидно. Прозрачное шифрование базы означает, что прежде, чем быть записанной на диск, любая ее страница автоматически шифруется, и, наоборот, при чтении с диска в память страницы базы автоматически расшифровываются. Перед тем, как отправиться клиенту, данные попадают в память, т.е. становятся расшифрованными, и в таком виде они будут переданы по сети, если не предпринять соответствующих мер. Злоумышленник не сможет прочитать файлы баз данных, если он, скажем, он похитит диск с ними, но он сможет получить доступ к данным, если перехватит их в процессе передачи по сети.
Для эксперимента возьмем SQL Server 2008 R2, установленный экземпляром по умолчанию на хост 192.168.0.1. Будем перехватывать сетевой трафик между ним и клиентом 192.168.0.2 при помощи сниффера Microsoft Network Monitor 3.4 подобно тому, как мы уже проделывали в посте «Как удаленно обнаружить экземпляры SQL Server на машине? Часть III».
Предварительно зашифруем базу. Чтобы зашифровать базу, в ней требуется создать DATABASE ENCRYPTION KEY. В BOL говорится, что ключ шифрования БД может быть создан на основе серверного сертификата или серверного же (т.е. созданного в БД master) асимметричного ключа. Следует иметь в виду, что абы какой асимметричный ключ для этого не годится:
use master
if exists (select 1 from sys.asymmetric_keys where name = 'AsmKeyForDBEncryptionKey') drop asymmetric key AsmKeyForDBEncryptionKey
create asymmetric key AsmKeyForDBEncryptionKey with algorithm = RSA_1024 encryption by password = 'Abra Cad@bra'
use TDE
create database encryption key with algorithm = AES_128 encryption by server asymmetric key AsmKeyForDBEncryptionKey
Msg 33120, Level 16, State 1, Line 5
In order to encrypt the database encryption key with an asymmetric key, please use an asymmetric key that resides on an extensible key management provider.
Скрипт 1
Требуется ключ, поставляемый провайдером расширенного управления ключами, в роли которого может выступать, например, аппаратный модуль безопасности (HSM). Поскольку ЕКМного провайдера у меня нет, остается создать серверный сертификат. Абы какой сертификат для этого не годится. Если новый сертификат будет защищен паролем, будет ошибка
Msg 33101, Level 16, State 1, Line 2
Cannot use certificate 'CertForDBEncryptionKey', because its private key is not present or it is not protected by the database master key.
Поэтому перед созданием сертификата в БД master следует предварительно создать там же мастер-ключ базы. Отсутствие в команде создания сертификата явной фразы ENCRYPTION BY PASSWORD = ... означает, что по умолчанию он как раз будет защищен мастер-ключом базы:
use TDE
--Если база TDE была зашифрована, предварительно расшифровываем ее и удаляем ключ шифрования базы
if exists (select * from sys.dm_database_encryption_keys where database_id = DB_ID('TDE') and encryption_state = 3)
begin
alter database TDE set encryption off
--Расшифровка базы - процесс не мгновенный. Ждем, пока она придет из состояния 3 в 1 (5 - расшифровывается, см. http://msdn.microsoft.com/ru-ru/library/bb677274.aspx)
while (select encryption_state from sys.dm_database_encryption_keys where database_id = DB_ID('TDE')) <> 1 waitfor delay '00:00:01'
end
if exists (select 1 from sys.dm_database_encryption_keys where database_id = DB_ID('TDE')) drop database encryption key
use master
--Создаем мастер-ключ базы master, которым защищаем серверный сертификат, которым будет защищаться ключ шифрования базы TDE
if exists (select 1 from sys.certificates where name = 'CertForDBEncryptionKey') drop certificate CertForDBEncryptionKey
if exists (select 1 from sys.symmetric_keys where name = '##MS_DatabaseMasterKey##') drop master key
create master key encryption by password = 'Abra Cad@bra'
create certificate CertForDBEncryptionKey with subject = 'Серверный сертификат для ключа шифрования БД', expiry_date = '2011-12-31'
use TDE
--Создаем новый ключ шифрования базы TDE и зашифровываем ее
create database encryption key with algorithm = AES_128 encryption by server certificate CertForDBEncryptionKey
alter database TDE set encryption on
Скрипт 2
if exists (select 1 from sys.tables where name = 'Salary') drop table Salary
create table Salary (id int identity, Employee nvarchar(50), Wage int)
insert Salary(Employee, Wage) values ('Manager', 1000000), ('Janitor', 1)
Скрипт 3
Пострадавшие компании не особенно любят распространяться, как именно с ними произошла неприятность навроде утечки данных. Они вообще предпочитают не предавать этот факт огласке по соображениям сохранения репутации, и все подробности, как правило, остаются за рамками внутреннего расследования. Однако большинство громких информационных краж не требовали виртуозного хакерского мастерства, а осуществлялись нехитрыми методами сотрудниками этих компаний, перекупленными или заранее внедренными. Предположим, такой крот решил похитить таблицу Salary, чтобы передать ее конкурентам или просто скомпрометировать фирму. Права доступа к колонке Зарплата у него, понятно, нет. Ежели он, аки тать, под покровом нощи, прокрадется в серверную и похитит диск или сделает копию базы или еще каким-либо образом получит доступ к ее файлам, то не сможет их прочесть ни SSMS, никаким другим средством вплоть до бинарного редактора, т.к. она зашифрована – см. все ту же демонстрацию TDE, скриншоты Visual Studio Hex Editor до и после шифрования базы. Тогда, находясь в пределах той же локальной сетки, он начинает снифферить траффик между SQL Server и клиентами в разгар трудовых будней. Я по бедности за неимением другой машины запущу сниффер на клиенте 192.168.0.2. Это избавит от необходимости включения на сниффере режима «промискуитета» (P-Mode) и, возможно, некоторой физической возни с сетью. К тому же, цель данной статьи состоит не в том, чтобы взаправду попятить данные, а только проиллюстрировать необходимость их защиты в процессе передачи даже при наличии зашифрованной базы. В качестве ресурсов на тему перехвата внешнего по отношению к машине трафика можно почитать статью «How to monitor remote traffic», а на тему использования Network Monitor 3.x можно порекомендовать курс видеолекций и блог, тоже на технете.
Открываем Network Monitor, говорим New Capture
Рис.1
В настройках фильтра захвата я пока отметил только сетевой адаптер, по которому, как я ожидаю, будет происходить соединение с SQL Server.
Рис.2
Нажимаем Apply, закрываем окно настроек фильтра захвата, нажав кнопку Close, и в главном окне Network Monitor в панели управления нажимаем кнопку Start (F5), чтобы начать процесс захвата сетевых пакетов. Если больше ничего про приложение неизвестно, остается запастись терпением и ждать, когда оно проявит себя, рано или поздно пообщавшись с SQL Server.
Рис.3
Клиентским приложением в моем случае является SQL Server Management Studio на 192.168.0.2, соединенная с отдельно стоящим (192.168.0.1) SQL Serverом. Действительно, вот она не утерпела и сообщила ему (Keep alive) о своем присутствии в добром здравии. Тот отвечает (Keep alive ack), что принял к сведению. Запаузим сбор пакетов.
Установим Capture Filter, чтобы целенаправленно собирать интересующие пакеты. SQL Server находится по адресу 192.168.0.1 и общается по порту 1433. Кликнем правой кнопкой на адрес 192.168.0.1 в колонке Source раздела Frame Summary и из контекстного меню выберем Add ‘Source’ to Display Filter.
Рис.4
Теперь добавим в фильтр порт, для чего спустимся в раздел Frame Details, раскроем секцию TCP, кликнем правой кнопкой на SrcPort:1433 и, аналогично, выберем Add Selected Value to Display Filter:
Рис.5
Исправим 16-ричное значение на десятичное, а союз OR – на AND, т.к. оба условия должны выполняться вместе.
Для полноты картины нас будут интересовать не только ответы SQL Server, но и поступающие на него запросы, т.е. совершенно симметрично добавляем 192:168.0.1:1433 в кач-ве назначения. Плюс к тому, чтобы сузить картину, добавим в фильтр, что нас интересует взаимодействие с SQL Server со стороны вполне определенного клиентского приложения, в нашем случае – ssms.exe. Аналогично Рис.4 добавляем в фильтр условие на имя процесса. В результате получается вот такой замечательный фильтр
(Source == "192.168.0.1" and TCP.SrcPort == 1433 OR Destination == "192.168.0.1" and TCP.DstPort == 1433) and Conversation.ProcessName == "Ssms.exe"
Скрипт 4
который, я считаю, заслуживает быть сохраненным:
Рис.6
Теперь говорим Clear Text и применяем сохраненный фильтр как Capture Filter. Разница между фильтрами Capture и Display, как нетрудно догадаться, состоит в том, что Display Filter фильтрует уже собранные записи, а Capture Filter просто не собирает то, что не удовлетворяет его условию, т.е. срабатывает раньше. Кликаем на Capture Settings, как мы уже делали на правом рис. на Рис.1, говорим Load Filter и загружаем фильтр, сохраненный на предыдущем шаге:
Рис.7
После чего нажимаем Apply, Close и вновь запускаем процесс сбора пакетов. Из SSMS выполняем запрос
select * from Salary
Скрипт 5
Чтобы не перегружать картину, Capture можно снова приостановить. Обратите внимание, что в разделе Frame Summary, в колонке Protocol Name появились фреймы протокола TDS:
Рис.8
Если вы забыли приостановить Capture в Network Monitor и фреймов, удовлетворяющих Capture Filter Скрипт 6 насобиралось довольно много, самое время применить Display Filter. Мы ищем ответ SQL Server на запрос Скрипт 7, который Network Monitor поймал на Рис.8. Понятно, что ответ идет следом за ним в той же сессии. Но если SQL Server интенсивно трудится и картина сильно зашумлена, для отыскания результатов запроса с зарплатами потребуется применить Display Filter. Мы знаем, что ответ тоже придет по протоколу TDS и в его результатах, скорее всего, будут должности сотрудников. Поэтому составляем Display Filter навроде
ProtocolName == "TDS" and ContainsBin(FrameData, 1, 'Janitor')
Скрипт 6
Плагины Contains и ContainsBin позволяют отыскивать не точное совпадение, а по фрагменту содержания. Третьим параметром ContainsBin выступает строка, которая должна входить в тело фрейма (первый параметр). Оставшийся второй параметр определяет, в каком формате ищем: ASCII, Unicode или HEX. Параметры ASCII и HEX воспринимаются нормально, однако поле Employee у нас nvarchar. Что писать, чтобы обозначить Unicode, я не знаю. В киношке NM3 Basic Filtering мужик написал uni, но тут же быстро стер. На uni она ругается. От безысходности пришлось догадаться, что обозначениям на самом деле соответствуют циферки 0, 1, 2, так что 1 – это юникод.
Нажимаем на кнопку Apply и получаем в разделе Frame Summary список фреймов, удовлетворяющих заданному условию.
Рис.9
Раскрываем секцию TDSServerResponseData – и перед нами открываются, как на ладони, все зарплаты: от менеджера до уборщика. Например, 0хF4240 – это в точности один миллион в десятичной системе счисления.
Рис.10
Таким образом, шифрование базы само по себе не предохраняет данные от перехвата в незашифрованном виде в процессе передачи. В следующей серии мы посмотрим, как защитить соединение с SQL Server.
Алексей Шуленин, Microsoft