TLS/SSL изнутри, а также почему нельзя настроить HTTPS для виртуальных хостов.
TLS (что есть Transport Layer Security), он же ранее известный как SSL (Secure Sockets Layer), на данный момент является стандартом де-факто для защиты протоколов транспортного уровня от различных методов вмешательства извне. Мало кто его не использует, но не уверен что все хорошо представляют себе, как оно на самом деле работает и какими возможностями обладает, кроме банального «как же, оно ведь шифрует канал между клиентом и сервером».
Протокол SSL разрабатывался компанией Netscape, целью его разработки являлось обеспечение безопасности данных, передаваемых на транспортном уровне модели ISO/OSI. Последняя версия протокола — SSL 3.0 была опубликована в 1996 году, и послужила основой для разработки протокола TLS, которой занялась организация IETF, и результатом этой работы стал RFC 2246: The TLS protocol, version 1.0, опубликованный в 1999 году. На данный момент опубликован RFC 5246: The TLS protocol, version 2.0, который несколько расширяет функциональность первой версии, впрочем оставляя суть неизменной.
Функционирует TLS поверх транспортного протокола, например (и зачастую) TCP. Грубо говоря, он работает с двумя потоками данных, вне зависимости от их природы, — входящим и исходящим, и каждый из них преобразует в соответствующим образом зашифрованный поток (правда, тут точнее будет говорить «измененный», поскольку TLS разрешает и отсутствие шифрования передаваемых данных).
Работа протокола разделяется на два этапа: обмен ключами, и дальнейший обмен данными.
Первый этап проходит без каких-либо «полезных» данных, передаваемых от клиента к серверу и обратно, и служит для идентификации клиента и сервера, а также выбора алгоритмов и инициализации ключей для дальнейшего шифрования. На втором этапе идет просто обмен данными через установленное логическое соединение, где каждый пакет полезных данных шифруется (если шифрование включено), защищается при помощи MAC, и передается другой стороне через нижележащий протокол (TCP).
Перейдем к более подробному описанию первой части протокола, как наиболее важной.
Инициализируется общение между клиентом и сервером сообщением ClientHello, которое посылается клиентом серверу. В этом сообщении клиент перечисляет поддерживаемые им «алгоритмы защиты» (в порядке предпочтения), а также передает некоторые другие параметры (поддерживаемые алгоритмы сжатия данных, 28 байт случайных данных, которое потом будут использованы для генерации общего секрета, идентификатор сессии при желании ее восстановления, в общем про это все будет дальше по тексту). Каждый «алгоритм защиты», вернее cipher suite (если у кого есть идея как перевести на русский в два слова — пожалуйста сообщите, «набор алгоритмов» как-то не звучит), на самом деле идентифицирует три алгоритма:
1) алгоритм обмена ключами, благодаря которому у клиента и сервера после некоторых манипуляций появляются общие 48 байт разделенного секрета, которые позже используются для генерации ключей ко всем остальным алгоритмами (шифрования и MAC)
2) блочный или поточный алгоритм шифрования, используемые для шифрования данных
3) MAC — алгоритм, используемый для подсчета MAC-кода сообщения (идентифицируется хеш-алгоритмом, поскольку в стандарте описан только HMAC).
Получив это сообщение, сервер выбирает набор шифров, который будет использоваться, согласно своей таблице предпочитаемых cipher suites, и отсылает клиенту в сообщении ServerHello. Кроме выбранного набора, сервер также посылает свои сгенерированные случайные данные и идентификатор сессии. Сразу после этого сообщения сервер, в зависимости от выбранного набор алгоритмов, посылает (или не посылает) следующие сообщения: Certificate, ServerKeyExchange, CertificateRequest. В то время как ServerKeyExchange напрямую зависит от выбранного алгоритма обмена ключами, и его содержание нам на данном этапе неинтересно, Certificate & CertificateRequest весьма важны для тех, кто в общих чертах хочет понять как это все работает, но старается избежать излишних подробностей.
В сообщении Certificate сервер посылает свой X.509 сертификат, который удостоверяет аутентичность сервера, а в CertificateRequest — сервер требует от клиента также прислать свой сертификат, чтобы доверить его аутентичность.
Кроме сертификата, сервер также (в пакете ServerKeyExchange) присылает цифровую подпись данных, посылаемых в этом пакете, что позволяет убедиться, что данный пакет действительно прислан сервером, который владеет секретным ключом к присланному сертификату.
Да, тут надо напомнить (или написать), что x.509 сертификат есть привязка некоторого открытого ключа к некоторой сущности (человеку, организации, или, как в данном случае — серверу), которая владеет секретным ключом, соответствующим этому открытому. Сертификаты бывают само-заверенные (self-signed), когда человек сам его подписывает, и заверенные центром сертификации. Основной вопрос, который возникает при встрече с таким сертификатом — доверие к нему, и тут можно полагаться или на свои личные данные (как в случае само-заверенными сертификатами — кто угодно может генерировать такой сертификат), или на доверенные центры, которые налагают свою подпись на сертификат, тем самым как бы доказывая, что они проверили, и этот сертификат действительно соответствует тому-то и тому-то. Более подробно про то, как мы с этим сталкиваемся, будет дальше, а пока-что вернемся к ServerCertificate.
Итак, сервер присылает свой сертификат, и клиент проверяет, действительно ли он доверяет этому сертификату, и в случае отрицательного ответа прерывает сеанс связи. Если сервер потребовал сертификат у клиента, клиент обязан предьявить свой сертификат, иначе связь уже будет прервана сервером. Как правило, последнее используется довольно редко (ну, например в WebMoney), и клиенты аутентифицируются на сервере при помощи логина/пароля.
После получения этого всего клиент посылает свой пакет Certificate (при надобности), ClientKeyExchange, CertificateVerify (цифровая подпись, сгенерированная сертификатом клиента).
Все. После этого, при прошедших взаимных проверках, считается, что клиент и сервер обладают общим секретом, размерностью в 48 байт. Из этого разделенного секрета, при помощи переданных перед этим случайных данных, и некоторых констант, по некоторым правилам (которые я тут конечно не описываю, это чисто технические подробности, а статья уже и так длинная получилась), генерируются ключи для алгоритмов шифрования и проверки MAC-кода (это стоило написать раньше, в общем MAC-код это что-то вроде хеша, но который успешно можно проверить только обладая дополнительно некоторым ключом), и далее идет обмен данным как предусмотрено протоколом уровня приложения.
Для HTTPS — это посылается клиентом запрос, сервером генерируется ответ, и отсылается обратно.
Более наглядно и коротко функционирование протокола можно представить следующей схемой:

Итак, теперь как же и что мы можем увидеть или настроить в процессе использования TLS:
1. Обмен сертификатами. Если сервер присылает сертификат, который не заверен корневым центром сертификации, известным вашему компьютеру (телефону, смартфону, наручным часам, и т.д.), ваше устройство, в зависимости от настроек, но как правило спросит «а доверяете ли вы такому-то сертификату?», на что почти каждый бездумно ответит «да!», перечеркнув всю (ну разве что кроме банального анализа сниферами) безопасность, предлагаемую протоколом TLS. Как например сделано в WebMoney — я был сильно удивлен, когда они предложили мне *свой* рутовый сертификат, который я ничем не мог проверить, кроме ихнего сайта.
2. Сертификат сервера. А как мы собственно узнаем, что сертификат соответствует этому сайту? Узнаем, потому как для TLS сертификатов в поле common name (CN=) прописывается имя сайта, которому он соответствует, которое браузер должен проверять, и в результате несоответствия говорить про ошибку проверки. Отсюда кстати и получаем проблему с тем, что на виртуальном хостинге на одном порту может быть только один HTTPS-сайт: ибо при инициализации соединения сервер должен послать сертификат для нужного хоста (если учесть что сервер это умеет, в апаче настраивается просто один сертификат, насколько я знаю), а нужный хост будет сообщен уже потом, в поле Host: HTTP-запроса.
На данный момент правда есть некоторое расширение TLS протокола Server Name Indication, описанное в RFC 3546 (спасибо IchBinFrei за наводку), однако насколько оно используется и поддерживается сейчас, нужно еще проверить.
Кроме того, сертификат может соответствовать не только одному имени сервера, а нескольким поддоменам, в виде *.host.com
3. Сертификат клиента. Ну, тут все просто — этот сертификат каким-то образом должен быть сохранен на сервер в списке доверяемых, или authority, выписавшая этот сертификат, должна быть доверяемой на сервере.
4. Быстродействие. Кроме того, что инициализация протокола требует трех посылок данных туда и назад (т.е. это уже 3 пинга), еще достаточно времени требует генерирование цифровой подписи (мы рассматриваем сторону сервера), а также разворачивание ключей для симметричных алгоритмов шифрования, что в сумме может занять более полсекунды (кстати, надо заметить, что генерирование DSA-подписи при равном размере с RSA-ключом, происходит раза в 2-4 быстрее). А если на странице при этом подгружается еще и 30 штук картинок, так тут самое время подумать про keep-alive, и про tls session resumption (т.е. продолжение сессии по ее идентификатору, который посылается в ClientHello ). Про это я к сожалению не имею еще данных, как оно на самом деле используется браузерами и серверами, и как будет время, протестирую и напишу.
Вот вроде бы пока что все, если есть вопросы — всегда рад ответить.
Протокол SSL разрабатывался компанией Netscape, целью его разработки являлось обеспечение безопасности данных, передаваемых на транспортном уровне модели ISO/OSI. Последняя версия протокола — SSL 3.0 была опубликована в 1996 году, и послужила основой для разработки протокола TLS, которой занялась организация IETF, и результатом этой работы стал RFC 2246: The TLS protocol, version 1.0, опубликованный в 1999 году. На данный момент опубликован RFC 5246: The TLS protocol, version 2.0, который несколько расширяет функциональность первой версии, впрочем оставляя суть неизменной.
Функционирует TLS поверх транспортного протокола, например (и зачастую) TCP. Грубо говоря, он работает с двумя потоками данных, вне зависимости от их природы, — входящим и исходящим, и каждый из них преобразует в соответствующим образом зашифрованный поток (правда, тут точнее будет говорить «измененный», поскольку TLS разрешает и отсутствие шифрования передаваемых данных).
Работа протокола разделяется на два этапа: обмен ключами, и дальнейший обмен данными.
Первый этап проходит без каких-либо «полезных» данных, передаваемых от клиента к серверу и обратно, и служит для идентификации клиента и сервера, а также выбора алгоритмов и инициализации ключей для дальнейшего шифрования. На втором этапе идет просто обмен данными через установленное логическое соединение, где каждый пакет полезных данных шифруется (если шифрование включено), защищается при помощи MAC, и передается другой стороне через нижележащий протокол (TCP).
Перейдем к более подробному описанию первой части протокола, как наиболее важной.
Инициализируется общение между клиентом и сервером сообщением ClientHello, которое посылается клиентом серверу. В этом сообщении клиент перечисляет поддерживаемые им «алгоритмы защиты» (в порядке предпочтения), а также передает некоторые другие параметры (поддерживаемые алгоритмы сжатия данных, 28 байт случайных данных, которое потом будут использованы для генерации общего секрета, идентификатор сессии при желании ее восстановления, в общем про это все будет дальше по тексту). Каждый «алгоритм защиты», вернее cipher suite (если у кого есть идея как перевести на русский в два слова — пожалуйста сообщите, «набор алгоритмов» как-то не звучит), на самом деле идентифицирует три алгоритма:
1) алгоритм обмена ключами, благодаря которому у клиента и сервера после некоторых манипуляций появляются общие 48 байт разделенного секрета, которые позже используются для генерации ключей ко всем остальным алгоритмами (шифрования и MAC)
2) блочный или поточный алгоритм шифрования, используемые для шифрования данных
3) MAC — алгоритм, используемый для подсчета MAC-кода сообщения (идентифицируется хеш-алгоритмом, поскольку в стандарте описан только HMAC).
Получив это сообщение, сервер выбирает набор шифров, который будет использоваться, согласно своей таблице предпочитаемых cipher suites, и отсылает клиенту в сообщении ServerHello. Кроме выбранного набора, сервер также посылает свои сгенерированные случайные данные и идентификатор сессии. Сразу после этого сообщения сервер, в зависимости от выбранного набор алгоритмов, посылает (или не посылает) следующие сообщения: Certificate, ServerKeyExchange, CertificateRequest. В то время как ServerKeyExchange напрямую зависит от выбранного алгоритма обмена ключами, и его содержание нам на данном этапе неинтересно, Certificate & CertificateRequest весьма важны для тех, кто в общих чертах хочет понять как это все работает, но старается избежать излишних подробностей.
В сообщении Certificate сервер посылает свой X.509 сертификат, который удостоверяет аутентичность сервера, а в CertificateRequest — сервер требует от клиента также прислать свой сертификат, чтобы доверить его аутентичность.
Кроме сертификата, сервер также (в пакете ServerKeyExchange) присылает цифровую подпись данных, посылаемых в этом пакете, что позволяет убедиться, что данный пакет действительно прислан сервером, который владеет секретным ключом к присланному сертификату.
Да, тут надо напомнить (или написать), что x.509 сертификат есть привязка некоторого открытого ключа к некоторой сущности (человеку, организации, или, как в данном случае — серверу), которая владеет секретным ключом, соответствующим этому открытому. Сертификаты бывают само-заверенные (self-signed), когда человек сам его подписывает, и заверенные центром сертификации. Основной вопрос, который возникает при встрече с таким сертификатом — доверие к нему, и тут можно полагаться или на свои личные данные (как в случае само-заверенными сертификатами — кто угодно может генерировать такой сертификат), или на доверенные центры, которые налагают свою подпись на сертификат, тем самым как бы доказывая, что они проверили, и этот сертификат действительно соответствует тому-то и тому-то. Более подробно про то, как мы с этим сталкиваемся, будет дальше, а пока-что вернемся к ServerCertificate.
Итак, сервер присылает свой сертификат, и клиент проверяет, действительно ли он доверяет этому сертификату, и в случае отрицательного ответа прерывает сеанс связи. Если сервер потребовал сертификат у клиента, клиент обязан предьявить свой сертификат, иначе связь уже будет прервана сервером. Как правило, последнее используется довольно редко (ну, например в WebMoney), и клиенты аутентифицируются на сервере при помощи логина/пароля.
После получения этого всего клиент посылает свой пакет Certificate (при надобности), ClientKeyExchange, CertificateVerify (цифровая подпись, сгенерированная сертификатом клиента).
Все. После этого, при прошедших взаимных проверках, считается, что клиент и сервер обладают общим секретом, размерностью в 48 байт. Из этого разделенного секрета, при помощи переданных перед этим случайных данных, и некоторых констант, по некоторым правилам (которые я тут конечно не описываю, это чисто технические подробности, а статья уже и так длинная получилась), генерируются ключи для алгоритмов шифрования и проверки MAC-кода (это стоило написать раньше, в общем MAC-код это что-то вроде хеша, но который успешно можно проверить только обладая дополнительно некоторым ключом), и далее идет обмен данным как предусмотрено протоколом уровня приложения.
Для HTTPS — это посылается клиентом запрос, сервером генерируется ответ, и отсылается обратно.
Более наглядно и коротко функционирование протокола можно представить следующей схемой:
Итак, теперь как же и что мы можем увидеть или настроить в процессе использования TLS:
1. Обмен сертификатами. Если сервер присылает сертификат, который не заверен корневым центром сертификации, известным вашему компьютеру (телефону, смартфону, наручным часам, и т.д.), ваше устройство, в зависимости от настроек, но как правило спросит «а доверяете ли вы такому-то сертификату?», на что почти каждый бездумно ответит «да!», перечеркнув всю (ну разве что кроме банального анализа сниферами) безопасность, предлагаемую протоколом TLS. Как например сделано в WebMoney — я был сильно удивлен, когда они предложили мне *свой* рутовый сертификат, который я ничем не мог проверить, кроме ихнего сайта.
2. Сертификат сервера. А как мы собственно узнаем, что сертификат соответствует этому сайту? Узнаем, потому как для TLS сертификатов в поле common name (CN=) прописывается имя сайта, которому он соответствует, которое браузер должен проверять, и в результате несоответствия говорить про ошибку проверки. Отсюда кстати и получаем проблему с тем, что на виртуальном хостинге на одном порту может быть только один HTTPS-сайт: ибо при инициализации соединения сервер должен послать сертификат для нужного хоста (если учесть что сервер это умеет, в апаче настраивается просто один сертификат, насколько я знаю), а нужный хост будет сообщен уже потом, в поле Host: HTTP-запроса.
На данный момент правда есть некоторое расширение TLS протокола Server Name Indication, описанное в RFC 3546 (спасибо IchBinFrei за наводку), однако насколько оно используется и поддерживается сейчас, нужно еще проверить.
Кроме того, сертификат может соответствовать не только одному имени сервера, а нескольким поддоменам, в виде *.host.com
3. Сертификат клиента. Ну, тут все просто — этот сертификат каким-то образом должен быть сохранен на сервер в списке доверяемых, или authority, выписавшая этот сертификат, должна быть доверяемой на сервере.
4. Быстродействие. Кроме того, что инициализация протокола требует трех посылок данных туда и назад (т.е. это уже 3 пинга), еще достаточно времени требует генерирование цифровой подписи (мы рассматриваем сторону сервера), а также разворачивание ключей для симметричных алгоритмов шифрования, что в сумме может занять более полсекунды (кстати, надо заметить, что генерирование DSA-подписи при равном размере с RSA-ключом, происходит раза в 2-4 быстрее). А если на странице при этом подгружается еще и 30 штук картинок, так тут самое время подумать про keep-alive, и про tls session resumption (т.е. продолжение сессии по ее идентификатору, который посылается в ClientHello ). Про это я к сожалению не имею еще данных, как оно на самом деле используется браузерами и серверами, и как будет время, протестирую и напишу.
Вот вроде бы пока что все, если есть вопросы — всегда рад ответить.



комментарии (52)