Partager via


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

Продолжение. Начало здесь.

Штатная защита соединений в SQL Server обеспечивается благодаря протоколу TLS (Transport Layer Security) и его предтечe SSL (Secure Sockets Layer), которые садятся поверх транспортного уровня (скажем, ТСР) и шифруют передаваемые в нем пакеты. Если я еще помню модель OSI, этот уровнь называется сеансовый. TDS живет на верхнем, прикладном уровне, и ему, по большому счету, должно быть все равно, как передавались данные – в шифрованном виде или открытом. Процедуру установления соединения по протоколу TLS можно прочитать в википедии. В процессе TLS-рукопожатия сервер предъявляет клиенту свой сертификат. Шифрование на основе сертификатов – это практически то же самое, что асимметричное шифрование, то есть открытый ключ, раздаваемый всем желающим, закрытый ключ, каковой держится в строгом секрете, плюс метаданные, описывающие, кто выдал означенный сертификат, на какой срок и т.д., записанные в формате, определяемом некоторым стандартом (X.509). Клиент внимательно на них смотрит и решает, можно ли ему доверять. Если сервер вызывает у клиента доверие, клиент генерит случайную последовательность, шифрует ее открытым ключом и отправляет на сервер. По определению асимметричного шифрования, расшифровать ее может только сервер своим закрытым ключом. Эта последовательность называется сеансовым ключом, и им дальше производится симметричное шифрование данных, которыми обмениваются сервер с клиентом.

Итак, для начала у SQL Server должен быть сертификат. Сертификат у SQL Server есть всегда, даже если у него нет никаких сертификатов. Oтличная фраза получилась! Стоило начать со слов «товарищи курсанты». Согласно военного устава, то бишь BOL, «SQL Server всегда шифрует сетевые пакеты, связанные с входом в систему. Если сертификат не был предоставлен на сервере при запуске, SQL Server создает самозаверяющий сертификат, который используется для расшифровки пакетов входа». Чтобы в этом убедиться, достаточно заглянуть в Error Log:

image

Рис.1

Это сертификат, который он выдает при старте себе, любимому, для шифрования логина и пароля, ежели под рукой нет ничего более пристойного.

Я, как и в прошлый раз, буду коннектиться к SQL Server (192.168.0.1) с клиента Windows 7 (192.168.0.2). На клиенте открываю Network Monitor и, поскольку в этот раз собираюсь соединяться с SQL Server из PowerShell, то в Capture Filter (Скрипт 4 из прошлой серии) заменяю Conversation.ProcessName с "Ssms.exe" на "PowerShell_ISE.exe". Теперь, как и обещано в фильтре, иду в Start -> All Programs -> Accessories -> Windows PowerShell -> Windows PowerShell ISE и выполняю следующий простой скрипт:

[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"

$cnn.Open()

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

$cmd.ExecuteScalar()

$cnn.Close()

image

Рис.2

В Network Monitor тем временем наблюдается:

image

Рис.3

что хотя и SQL Batch и Response на него в TDS читаются открытым текстом, логин и пароль из сетевого перехвата почерпнуть не удастся. На рисунке обведены фреймы, где у них с клиентом началась любовь, и можно видеть, что попытка логона совершается по зашифрованному протоколу TLS, о котором шла речь в начале поста. Сертификат, который SQL Server как честная девушка показывает клиенту на этапе знакомства TLS-handshake, есть тот самый сертификат с Рис.1, который он сам себе нарисовал при старте.

Защитить соединение целиком при помощи такого сертификата не удастся - Encrypt=True в строке соединения с негодованием отвергается:

image

Рис.4

Единственный способ заставить его проглотить самопальный сертификат - это добавить в строку соединения TrustServerCertificate=True:

image

Рис.5

Вместо того, чтобы на каждом клиенте играться параметрами соединения Encrypt и TrustServerCertificate, их можно выставить централизованно для всех клиентских соединений при помощи SQL Server Configuration Manager (Start -> All Programs -> Microsoft SQL Server 2008 R2 -> Configuration Tools). Кликаем правой кнопкой по SQL Native Client Configuration и в Properties

image

Рис.6

видим знакомые нам по строке соединения Force Protocol Encryption и Trust Server Certificate:

image

Рис.7

Сразу возникает вопрос: как быть, если конфигурационные настройки SQL Native Client противоречат параметрам конкретной строки соединения? Например, на Рис.7 Trust Server Certificate выставлен в No, а на Рис.5 SqlConnection инициализируется с TrustServerCertificate=True. Ответ на него находится в статье BOL «Использование шифрования без проверки»:

Конфигурация «Принудительное шифрование протокола» SQL Native Client

Конфигурация «Доверять сертификату сервера» SQL Native Client

Строка соединения или атрибут соединения «Шифрование/использовать шифрование для данных»

Строка соединения или атрибут соединения «Надежный сертификат сервера»

Результат

Нет

Недоступно

Нет (по умолчанию)

Не учитывается

Шифрование отсутствует.

Нет

Недоступно

Да

Нет (по умолчанию)

Шифрование применяется только при наличии подтверждаемого сертификата сервера, в противном случае попытка соединения завершается неудачно.

Нет

Недоступно

Да

Да

Шифрование производится всегда, однако при этом может использоваться самозаверяющий сертификат сервера.

Да

Нет

Не учитывается

Не учитывается

Шифрование применяется только при наличии подтверждаемого сертификата сервера, в противном случае попытка соединения завершается неудачно.

Да

Да

Нет (по умолчанию)

Не учитывается

Шифрование производится всегда, однако при этом может использоваться самозаверяющий сертификат сервера.

Да

Да

Да

Нет (по умолчанию)

Шифрование применяется только при наличии подтверждаемого сертификата сервера, в противном случае попытка соединения завершается неудачно.

Да

Да

Да

Да

Шифрование производится всегда, однако при этом может использоваться самозаверяющий сертификат сервера.

TrustServerCertificate=True, будь то задано в конкретном соединении или сконфигурировано на уровне SQL Native Client, нельзя признать надежным способом, т.к. он позволяет провести типичную для систем с асимметричным шифрованием атаку типа «человек посередине», известную еще со времен Диффи-Хеллмана. На декабрьском семинаре 2007 г. Russian SQL Server User Group ее с успехом демонстрировали Николай Денищенко и Ян Либерман. По этой причине, например, при соединении с SQL Azure параметр TrustServerCertificate в обязательном порядке выставлен в False. По-хорошему, при работе с on-premise SQL Server тоже от греха подальше не стоит слепо доверять серверному сертификату, не говоря уже о том, что если рассматривать принудительное включение шифрования соединений не со стороны клиентов, а со стороны сервера, его просто невозможно сконфигурировать с самозаверяющим сертификатом. Обратим внимание на Рис.6 на Protocols for MSSQLSERVER строчкой выше. Выбрав аналогично ее свойства, можно сконфигурировать SQL Server, чтобы он сам шифровал все соединения по всем протоколам вместо того, чтобы задействовать шифрование по инициативе клиента, как рассматривалось до сих пор. Однако шифрование по инициативе сервера требует наличия честного сертификата. Самопальный он не берет в расчет:

image

Рис.8

В следующей серии мы создадим относительно честный, внешний по отношению к SQL Server сертификат, который ему подсунем, обеспечив включение форсированного шифрования соединений со стороны SQL Server, либо со стороны клиентов без необходимости доверять неудостоверенному серверному сертификату, что значительно повысит их устойчивость к перехвату.

 

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