Share via


Шифрование трафика между SQL Server и клиентом. Часть 4.

 

В предыдущей серии мы создали для SQL Server сертификат, который он подхватил при старте и который в отличие от самопального может использоваться не только для шифрования передачи логина/пароля, но и для закрытия всего соединения. С этим понятно. Давайте посмотрим более интересную ситуацию: что будет делать SQL Server, если мы ему создадим несколько сертификатов. Повторим команду Рис.2 из предыдущей серии. Теперь SQL Server потенциально является счастливым обладателем двух сертификатов:

image

Рис.1

Первый - это созданный ранее сертификат со знакомым нам хэшем (отпечатком большого пальца) a5 d8 1c 78 e9 18 88 99 30 33 fd 3e bb a7 6e 65 73 3a 66 96. Благодаря отпечатку мы убедились (см. Рис.8 предыдущего поста), что именно его подхватил SQL Server:

image

Рис.2

Второй - это свежесозданный сертификат:

image

Рис.3

Перестартуем SQL Server и посмотрим, как он на них отреагирует. Мы видим, что Error Log ничем не отличается от Рис.8 предыдущей серии. SQL Server пробежался по коллекции подходящих для него сертификатов, нашел первый и успокоился. Если мы хотим, чтобы для шифрования соединений использовался другой сертификат, то идем в SQL Server Configuration Manager, встаем на SQL Server Network Configuration \ Protocols for <Имя инстанса>, кликаем по этой строчке правой кнопкой, открываем Properties и в окне свойств - закладку Certificate. Обратите внимание, что в отличие от Рис.8 позапрошлой серии комбобокс выбора сертификата не пуст, а содержит оба сертификата, виденные нами на Рис.1:

image

Рис.4

Выбираем из списка доступных сертификатов тот, которым мы хотим защищать соединение. После выбора сертифката станет доступна кнопка View. Она открывает окно со свойствами сертификата, ровно такое, как мы видели (Рис.2, Рис.3) по двойному клику на сертификат в mmc (Рис.1). Ее можно нажать, чтобы убедиться, что мы выбрали именно тот сертификат, который хотели. Например, можно сверить thumbprint. Кнопка Clear говорит SQL Server не использовать никакой сертификат, даже при наличии доступных. Для шифрования логина/пароля будет использоваться первый из коллекции доступных, либо самосгенерированный, если доступных нет. Чтобы изменения вступили в силу, необходимо перезапустить сервис SQL Server.

Перейдем на закладку Flags. Здесь присутствуют две установки: Force Encryption и Hide Instance

image

Рис.5

Для чего нужна вторая, мы разбирали в посте «Как удаленно обнаружить экземпляры SQL Server на машине? Часть III». Если ее поставить в Yes, сервис SQL Server Browser не будет сообщать о данном экземпляре. Что означает первая, мы разбирали в позапрошлой серии. Если на закладке Certificate (Рис.4) подключен сертификат, то в случае Force Encryption = Yes, SQL Server будет в принудительном порядке шифровать все подключения к нему. Иначе (Force Encryption = No) будут шифроваться только те соединения, у которых в строке соединения явно задано Encrypt = True.

Иллюстрируем сказанное.

1. Имея подключенный на Рис.4 сертификат, ставим на Рис.5 Force Encryption = Yes, перезапускаем SQL Server. В Network Monitor, как и в позапрошлый раз, в Capture Settings отмечаем снизу адаптер, трафик по которому будем ловить, и задаем фильтр (Source == "192.168.0.1" and TCP.SrcPort == 1433 OR Destination == "192.168.0.1" and TCP.DstPort == 1433) and Conversation.ProcessName == "PowerShell_ISE.exe", жмем Apply и Close. Стартуем снифферинг. Выполняем Powershellьный скрипт Рис.2 из позапрошлой серии. Смотрим, что насобиралось в Network Monitor:

image

Рис.6

В ответ на select @@version SQL Server должен сообщить что-нибудь типа Microsoft SQL Server 2008 R2 (RTM) - 10.50.1600.1 (X64), ля-ля-ля. Попытаемся отыскать строку Microsoft SQL Server в перехваченном сетевом трафике. Жмем на кнопку Find в панели фреймов и в окне Find Frame по аналогии со Скрипт 4 первой серии пишем:

ContainsBin(FrameData, hex, "4D 00 69 00 63 00 72 00 6F 00 73 00 6F 00 66 00 74 00 20 00 53 00 51 00 4C 00 20 00 53 00 65 00 72 00 76 00 65 00 72 00")

image

Рис.7

Бинарщина в кавычках есть в точности 16-ричное представление строки Microsoft SQL Server. Дело в том, что плагин ContainsBin плохо ищет ASCII и Unicode-строки, в которых присутствует пробел, поэтому проще искать по 16-ричному содержанию. Ставим второй параметр hex (или 2), идем в панель hex details на Рис.3 позапрошлой серии, выделяем слова Microsoft SQL Server, в левой части (где коды), нажимаем правую кнопку и говорим Copy. Иногда получается J

image

Рис.8

Нажимаем кнопку Find на Рис.7 и ничего не находим, потому что теперь данные не передаются в открытом виде.

image

Рис.9

По сравнению с Рис.3 позапрошлой серии видим, что вместо TDS:SQLBatch, в котором можно прочитать текст запроса, и TDS:Response, из которого можно поглядеть результат его выполнения, теперь присутствуют сплошь фреймы протокола TLS, которые мы наблюдали ранее только во время процесса аутентификации. Т.е. не только логин/пароль, но и все запросы, поступающие от клиента на SQL Server, и все результаты, которые SQL Server возвращает клиенту, передаются в зашифрованном виде. Это будет происходить для всех соединений, поскольку на сервере включена принудительная шифрация (Force Encryption = Yes).

2. Ставим на Рис.5 Force Encryption = No, перезапускаем SQL Server, нажимаем Stop и Start в Network Monitor, выполняем тот же Powershellьный скрипт. Повторяем поиск Рис.7 и все прекрасно находим в открытом виде.

image

Рис.10

Установленный на SQL Server сертифкат сам по себе не обеспечивает защиту клиентских соединений, если ни со стороны сервера (Force Encryption = Yes в SQL Server Configuration Manager), ни со стороны клиента это специально не оговорено.

3. Защита соединений по инициативе клиента может происходить путем конфигурирования SQL Native Client (Рис.6, 7 позапрошлой серии), при этом, как и в случае п.1, шифруются все клиентские соединения. Либо соединения можно шифровать выборочно, добавляя Encrypt=True в строку соединения.

Модифицируем строку соединения в скрипте PowerShell позапрошлой серии, чтобы она выглядела так:

Server=192.168.0.1,1433;Network Library=dbmssocn;User ID=sa;Password=p@ssw0rd;Encrypt=True;TrustServerCertificate=False

Переоткроем PowerShell, cнова запустим скрипт на выполнение и получим ошибку Рис.4 позапрошлой серии - A connection was successfully established with the server, but then an error occurred during the pre-login handshake. (provider: SSL Provider, error: 0 - The certificate chain was issued by an authority that is not trusted.) Все верно: хотя нынешний сертификат не является самосгенерированным SQL Server, мы создавали его при помощи утилиты makecert (Рис.2 прошлой серии) как самоудостоверящий. Вполне понятно, что у клиента возникли подозрения по поводу его достоверности. Можно, как и в случае с самосгенерированным SQL Server сертификатом, принудительно заставить клиента его принять и не вякать (TrustServerCertificate=True), однако, как мы помним, это чревато атакой man-in-the-middle. В данном случае нет нужды слепо доверять абы какому серверному сертификату. Можно оставить TrustServerCertificate=False и познакомить клиента с данным конкретным сертифкатом. Помните, что при генерации сертификата для SQL Server (Рис.2 прошлой серии) он скидывался в файл c:\temp\sql2008r2.cer. Берем этот файл, переносим его на клиентскую машину (192.168.0.2) и дважды кликаем по нему

image

Рис.11

Нажимаем кнопку Install Certificate и в качестве места хранения указываем Trusted Root Certification Authorities, как и рекомендовано на Рис.11:

image

Рис.12

image

Рис.13

Нажимаем кнопку Finish и соглашаемся с предупреждением о том, что этот сертификат не может быть проверен:

image

Рис.14

Откроем certmgr.msc на клиенте, чтобы убедиться, что сертифкат установился успешно:

image

Рис.15

Повторяем выполнение скрипта. На этот раз он выполняется нормально:

[System.Data.SqlClient.SqlConnection] $cnn = New-Object -TypeName System.Data.SqlClient.SqlConnection -argumentList "Server=192.168.0.1,1433;Network Library=dbmssocn;User ID=sa;Password=p@ssw0rd;Encrypt=True;TrustServerCertificate=False"

$cnn.Open()

[System.Data.SqlClient.SqlCommand] $cmd = New-Object -TypeName System.Data.SqlClient.SqlCommand -argumentList "select @@version", $cnn

$cmd.ExecuteScalar()

$cnn.Close()

image

Рис.16

В Network Monitor наблюдаем, что, как и в случае Рис.6, соединение успешно зашифровано:

image

Рис.17

 

Алексей Шуленин, Microsoft

 

Домашнее задание. Найти ответ на вопрос Дмитрия в блоге Яна.