
Сегодня 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 CAuser@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 CAuser@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 G3user@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!
