
Сегодня HTTPS считается де-факто стандартом для безопасного сёрфинга веб-страниц, но знаете ли вы о подводных камнях, на которые мы натыкаемся в самый неудобный момент? Сегодняшняя статья расскажет о самой главной из них, а так же о способе её исправления.
Немного об устройстве TLS и общении через этот протокол
История протоколов TLS и SSL берёт своё начало с разработки компанией Netscape Communications протокола SSL (Secure Sockets Layer), который в свою очередь придумали на колене для безопасной передачи данных в интернете. Первая версия, SSL 1.0, так и не увидела свет из-за серьёзных уязвимостей, а SSL 2.0 не рекомендовалась к использованию из-за слабого шифрования. SSL 3.0 стал отправной точкой для стандартизации IETF протокола TLS 1.0 в 1999 году. Позднее вышли новые версии: TLS 1.1 (2006), TLS 1.2 (2008) и TLS 1.3 (2018), каждая из которых улучшала безопасность, убирала устаревшие алгоритмы и оптимизировала процесс установки соединения.
Протокол TLS обеспечивает защищённую передачу данных посредством комбинации асимметричного и симметричного шифрования. Процесс начинается с так называемого TLS-рукопожатия (TLS handshake), в ходе которого клиент и сервер аутентифицируют друг друга и согласовывают параметры защиты. Сначала клиент отправляет список поддерживаемых криптографических наборов (cipher suites), а сервер выбирает наиболее подходящий из них. Затем сервер предоставляет цифровой сертификат X.509, подписанный удостоверяющим центром (CA), подтверждая свою подлинность.
После проверки сертификата клиент и сервер устанавливают общий симметричный ключ, используя алгоритмы, такие как Диффи-Хеллмана (ECDHE) или RSA (в устаревших версиях). Этот ключ применяется для шифрования передаваемых данных, обеспечивая их конфиденциальность и целостность. В TLS 1.3 процесс рукопожатия оптимизирован за счёт отказа от ненадёжных алгоритмов и уменьшения количества обменов данными, что ускоряет установку соединения и снижает уязвимость к атакам типа downgrade.
Однако, несмотря на все улучшения и обновления протоколов SSL и TLS, остаётся нерешённой одна из ключевых проблем, возникшая ещё на этапе разработки и публикации этих стандартов. Об этой проблеме пойдёт речь далее.
Самая ужасная проблема корневых сертификатов
Все мы слышали о так называемых удостоверяющих центрах (Certificate Authority, CA), которые выпускают и подписывают сертификаты, а также о корневых удостоверяющих центрах (Root CA). Проблема таких CA в том, что сертификаты имеют ограниченный срок действия и могут быть отозваны, но при этом не существует механизма их автоматической замены с сохранением доверия. Это значит, что если, например, у вас корневой сертификат от DigiCert истекает в 2025 году, то вы не сможете его заменить без дополнительных манипуляций.
Эта проблема затрагивает все существующие устройства и операционные системы, поскольку так и не придумали действительно универсальное решение, которое предполагало бы сохранение доверия пользователя без каких-либо сторонних сервисов, репозиториев, списков и прочих «костылей». Конечно, можно вспомнить такие протоколы, как ACME и OSCP/CRL, предназначенные для управления сертификатами. Казалось бы, раз есть механизмы для обновления обычных сертификатов, то должен быть и стандарт для автоматического обновления корневых. Но, к сожалению, нет.
Как «решить» проблему?
Многим пользователям «по барабану» на всё это доверие и прочие концепции безопасности TLS — им важно лишь одно: чтобы все сервисы продолжали работать без перебоев. Какие решения существуют сегодня?
В Microsoft Windows и Apple macOS/iOS обновление сертификатов происходит при помощи менеджера обновлений самой ОС. Это, конечно, не самый надёжный источник с точки зрения доверия, но, тем не менее, он позволяет смягчить или даже устранить проблему.
В Android 14 и выше, помимо обновлений ОС, за обновление хранилища сертификатов теперь отвечает Google Play. Это же приложение уже управляет обновлениями драйверов, применением патчей безопасности и прочими аспектами, связанными с «безопасностью устройства». Насколько можно доверять Google в этом вопросе — решать только вам.
Что касается семейства дистрибутивов Linux и систем BSD, то обычно их репозиторий содержит пакет
ca-certificates
илиlibcrypto
, в котором находится либо нетронутый набор корневых сертификатов, либо объединённый файл (например,cert.pem
в OpenBSD). Здесь вся ответственность за актуальность сертификатов ложится на сопровождающего пакета.
Все эти механизмы позволяют, скажем так, обходить проблему истечения срока действия корневых сертификатов. А если у пользователя есть достаточный уровень технических знаний и нужные инструменты — например, понимание работы CA в ОС или возможность задействовать «кастомные» прошивки — он может вообще не замечать существование этой проблемы.
Но есть ли способ избавиться от неё раз и навсегда без всех этих костылей?
Решение проблемы
Концепция исправления проблемы без лишних телодвижений
Исходя из этой проблемы, нам необходим безопасный и универсальный механизм обновления корневых сертификатов, независимый от операционной системы и её поставщиков. Такой механизм должен основываться исключительно на самих данных сертификата и ни на чём больше.
При получении нового сертификата он должен автоматически сверяться со старым, чтобы убедиться в их соответствии. В идеале же сам новый сертификат должен быть подписан старым и доступен через специальный API.
Разработка механизма
Допустим, что в корневых сертификатах всегда присутствует параметр OU в Distinguished Name (DN), содержащий доменное имя удостоверяющего центра. В этом случае задача сводится к правильному запросу непосредственно к CA для получения актуального корневого сертификата. Однако на практике это не всегда так: далеко не каждый сертификат включает OU, а ссылки на CRL (списки отзыва) могут вовсе отсутствовать.
Приведу пример выхлопа step-cli на Fedora Workstation 41 с несколькими случайными сертификатами:
user@fedora:~$ step certificate inspect /etc/ssl/certs/DigiCert_Global_Root_CA.pem
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 10944719598952040374951832963794454346 (0x083be056904246b1a1756ac95991c74a)
Signature Algorithm: SHA1-RSA
Issuer: C=US,O=DigiCert Inc,OU=www.digicert.com,CN=DigiCert Global Root CA
Validity
Not Before: Nov 10 00:00:00 2006 UTC
Not After : Nov 10 00:00:00 2031 UTC
Subject: C=US,O=DigiCert Inc,OU=www.digicert.com,CN=DigiCert Global Root CA
user@fedora:~$ step certificate inspect /etc/ssl/certs/b66938e9.0
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 9751836167731051554232119481456978597 (0x075622a4e8d48a894df413c8f0f8eaa5)
Signature Algorithm: SHA1-RSA
Issuer: C=US,O=SecureTrust Corporation,CN=Secure Global CA
Validity
Not Before: Nov 7 19:42:28 2006 UTC
Not After : Dec 31 19:52:06 2029 UTC
Subject: C=US,O=SecureTrust Corporation,CN=Secure Global CA
user@fedora:~$ step certificate inspect /etc/ssl/certs/064e0aa9.0
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 390156079458959257446133169266079962026824725800 (0x445734245b81899b35f2ceb82b3b5ba726f07528)
Signature Algorithm: SHA256-RSA
Issuer: C=BM,O=QuoVadis Limited,CN=QuoVadis Root CA 2 G3
Validity
Not Before: Jan 12 18:59:32 2012 UTC
Not After : Jan 12 18:59:32 2042 UTC
Subject: C=BM,O=QuoVadis Limited,CN=QuoVadis Root CA 2 G3
user@fedora:~$ step certificate inspect /etc/ssl/certs/8794b4e3.0
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 87493402998870891108772069816698636114 (0x41d29dd172eaeea780c12c6ce92f8752)
Signature Algorithm: ECDSA-SHA384
Issuer: C=US,O=Internet Security Research Group,CN=ISRG Root X2
Validity
Not Before: Sep 4 00:00:00 2020 UTC
Not After : Sep 17 16:00:00 2040 UTC
Subject: C=US,O=Internet Security Research Group,CN=ISRG Root X2
Но что, если при создании нового протокола все сертификаты придётся перевыпустить с обязательным параметром OU или с дополнительным блоком информации для автоматического обновления? Остановимся на этом варианте, но теперь нам потребуется ранее не виданное дополнительное ПО.
Представим, что теперь доступ к CA возможен только через специальный API, вроде того же java.security.cert
, который работает через выделенный агент/демон/сервис на самом устройстве. Теперь прямой доступ к файлам корневых сертификатов отсутствует, а значит, никакое ПО не сможет незаметно подменить сертификаты или добавить свои собственные без ведома пользователя.
Вариант Proof-of-Concept программы для обновления одного сертификата на языке Python доступен на GitHub для ознакомления. Принцип её работы прост:
Анализирует DN сертификата.
Получает ссылку на сайт CA либо из OU, либо из дополнительного параметра X.509, а бонусом лишь в рамках Proof-of-Concept ещё и из внутренних ссылок.
Запрашивает у CA актуальный сертификат.
Проверяет его срок действия и соответствие старому.
При успешной верификации заменяет старый сертификат на новый.
Очевидно, что ради безопасности подобное ПО стоит переписать на компилируемый язык, а сам процесс стандартизировать. Но пока мы говорим лишь о концепте, верно?
Подведём итоги
Создать подобный протокол всегда было возможно, а сегодня, когда многие корневые сертификаты приближаются к сроку истечения, это уже необходимость. Почему до сих пор никто не разработал универсальное решение — вопрос, который для меня остаётся загадкой.
Впрочем, доверие к такой системе обновления сертификатов вряд ли будет выше, чем к самим удостоверяющим центрам, но раз уж мы выбрали этот путь... Значит, пора убрать все лишние преграды, сделать процесс «установил и забыл» реальностью и сосредоточиться на действительно важных задачах, верно?
Как вам сегодняшняя статья и сама ситуация вокруг TLS? Обязательно поделитесь мнением в комментариях! Жду вашу критику как меня, так и инженеров SSL/TLS.
Всем peace!