Подробные статьи о технологиях, определяющих будущее.

Слабые ключи eMudhra: 5 мифов о PKI под вопросом

Доверенный CA выпустил приватные ключи, взламываемые за миллисекунды. Пять мифов о PKI, генерации ключей и аудите на примере инцидента eMudhra.

Огромная открытая дверь хранилища, запертая лишь крошечным игрушечным замком
Семнадцать месяцев безопасности центра сертификации держались на восьми битах энтропии.

Один из приватных ключей центра сертификации eMudhra равен числу 4. Не 384-битному числу, которое начинается с четверки, а просто 4, дополненному 47 нулевыми байтами, чтобы выглядеть прилично. Это ошибка генерации ключа CA того типа, которую я считал невозможной. Она пережила аудиты, перекрестную подпись от Sectigo и семнадцать месяцев в инфраструктуре, близкой к продакшену, пока кто-то не попробовал очевидное: перебрать один байт. Вот пять убеждений о PKI, которые этот инцидент окончательно убивает.

Миф 1: «Структурно валидный сертификат — это безопасный сертификат»

Каждая автоматическая проверка в пайплайне говорила, что эти сертификаты в порядке. И в узком, бесполезном смысле они были правы. Публичный ключ, соответствующий d = 4, — вполне корректная точка на кривой P-384. ASN.1-кодирование разбирается, подпись проверяется, цепочка строится. Любой линтер, любой структурный валидатор, любой чек-лист соответствия, который проходит по сертификату и подтверждает, что он правильно оформлен, пропустит их, потому что они правильно оформлены.

В этом и ловушка. Структурная валидация отвечает на вопрос «правильно ли оформлен этот сертификат?». Она не отвечает на вопрос «правильно ли сгенерирован внутренний ключ?». Для ключей на эллиптических кривых это почти совершенно независимые свойства. Приватный ключ — это просто скаляр d; публичный ключ — это d × G, где G — генераторная точка кривой. Вся защита держится на том, что восстановить d по публичной точке можно только решив задачу дискретного логарифмирования, но лишь при условии, что d действительно выбран из полного 384-битного пространства. Выбери d из 8 бит, и «невозможная» задача превращается в цикл, который делает не больше 255 итераций.

В самом сертификате ничего на это не намекает. Публичная точка, полученная из d = 4, выглядит так же случайно, как и точка от правильно сгенерированного ключа. Глазом не определить. Линтером тоже. Узнать можно только перебрав маленькие значения и известные слабые паттерны, что и произошло: исследователь прогнал набор тестов paranoid_crypto от Google по корпусу Common CA Database сразу, без настройки, и получил попадание. Проверка, которая занимает миллисекунды на ноутбуке, работает с ключами, на которых держится доверие в реальных браузерах.

Итог первого урока: валидность необходима, но далеко не достаточна. Если ваше тестирование PKI заканчивается на «сертификат парсится и строит цепочку», вы тестируете оболочку и игнорируете содержимое.

Миф 2: «Базовые требования и аудиты поймают ошибки генерации ключей»

Центры сертификации работают по Baseline Requirements форума CA/Browser и проходят аудит по критериям WebTrust или ETSI. Есть документация по церемониям, требования к HSM, требования к длине ключей. Ключи eMudhra были нужной длины, 384 бита на бумаге. Проблема аудита в том, что проверяется представление ключа, а не энтропия процесса, которым он был создан. Ключ, дополненный 47 нулевыми байтами, по-прежнему занимает 48 байт на диске.

Согласно собственному отчету eMudhra, опубликованному в тикете инцидента Mozilla, корневая причина звучит как черная комедия: ошибка в скрипте церемонии сгенерировала на HSM вторую, слабую пару ключей; у двух пар были похожие имена; человек выбрал неправильную для CSR. Три независимых сбоя, ошибка скрипта, коллизия имен и ошибка выбора, и ни один контроль ни разу не спросил: «Есть ли у этого ключа та энтропия, за которую мы заплатили HSM?»

Вот что должно тревожить каждого инженера, который ведет собственный внутренний PKI. Если публично доверенный CA, под давлением корневых программ и с аудитами по расписанию, может выпустить приватный ключ, равный 4, то что скрывается за вашим бэкендом Vault PKI или шаблонами Active Directory Certificate Services? Исправление дешевое, и нет никаких оправданий, чтобы его не внедрить. После генерации любого ключа, но до того как подписать им что-либо, прогоняйте его через paranoid_crypto или хотя бы через тривиальную проверку на адекватность:

from cryptography.hazmat.primitives.asymmetric import ec
CURVE_ORDER_BITS = 384
def sanity_check_private_key(key: ec.EllipticCurvePrivateKey) -> None:
d = key.private_numbers().private_value
# Reject keys whose effective entropy is far below the curve size.
# A healthy P-384 key should have a bit length close to 384.
if d.bit_length() < CURVE_ORDER_BITS - 32:
raise ValueError(
f"Suspiciously small private scalar: {d.bit_length()} bits"
)
# Also reject known bad values outright.
if d < 2**16:
raise ValueError("Private scalar is trivially brute-forceable")

Тридцать секунд работы. Это поймало бы все восемь ключей eMudhra. Аудиты проверяют, что процессы существуют; они не проверяют, сработали ли процессы в конкретный вторник в апреле. Это делает только тестирование результата.

Миф 3: «Скомпрометированный ключ CA можно локализовать»

Когда ключ CA «скомпрометирован» в привычном смысле, есть вор. Кто-то выкрал ключ в определенный момент, и можно оценить радиус поражения: сертификаты, выпущенные после кражи сторонами с украденным ключом. Неприятно, но ограниченно. Мы уже писали о том, что происходит, когда компрометация промежуточного CA ломает цепочку доверия, и стандартный план действий (отозвать, снять доверие, перевыпустить) предполагает «до» и «после».

У этого инцидента нет ни «до», ни «после». Ключ был фактически публичным с момента создания сертификата, потому что любой, кто смотрел на сертификат, мог восстановить его за миллисекунды. Нет события кражи, которое можно ограничить по времени. Окно охватывает весь срок жизни сертификатов, начиная с апреля 2025 года, и нет способа узнать, кто, если вообще кто-то, прошел в открытую дверь. Заявление eMudhra о том, что промежуточные CA не выпускали сертификаты конечных субъектов, подтвержденное логами Certificate Transparency, действительно обнадеживает в плане того, что эти CA делали. Но оно ничего не говорит о том, что мог бы оппортунистически сделать тот, у кого есть d=4, на сетевом пути.

Хуже того, сам ответ не может полностью сработать. Chrome и Firefox заблокировали промежуточные CA в течение нескольких часов через CRLSet и OneCRL, и обе требуют логирования в CT, так что поддельные TLS-сертификаты в этих браузерах все равно не прошли бы валидацию. Хорошо. Но эта защита заканчивается на границе браузера, и это подводит нас к следующему мифу.

Миф 4: «Если браузеры в безопасности, то и экосистема в безопасности»

Светящееся бронированное окно браузера, окруженное сотнями открытых подключенных устройств в темноте
Браузеры залатали за считаные часы; все остальное в экосистеме TLS по-прежнему уязвимо.

Браузеры — это лучший сценарий в экосистеме TLS, и они составляют меньшинство TLS-клиентов, которые важны вам с точки зрения эксплуатации. Подумайте, что на самом деле устанавливает HTTPS-соединения в вашей инфраструктуре: curl, requests в Python, Node, бинарники на Go, Java-сервисы, git, менеджеры пакетов, облачные SDK. У этих клиентов три свойства, которые плохо складываются вместе: они доверяют набору CA, лежащему на диске, они по умолчанию не проверяют отзыв сертификатов и не проверяют Certificate Transparency. Все три защиты, которые спасли пользователей Chrome, здесь отсутствуют.

Скотт Хелм показал это наглядно: с поддельным сертификатом для api.stripe.com, построенным на одном из слабых промежуточных CA eMudhra, стандартный curl выводил SSL certificate verify ok, Python успешно завершал рукопожатие, Node сообщал, что соединение авторизовано, а Windows CryptoAPI принимал цепочку. На каждой протестированной машине с Linux и macOS. Поддельный сертификат для любого имени хоста, принятый стандартными инструментами, из которых собраны ваши скрипты развертывания и CI-раннеры. Это не проблема браузеров, это проблема вашей инфраструктуры.

Карта уязвимостей шире, чем большинство людей себе представляет:

  • Установщики вида curl | sh и самообновления. rustup, nvm, установщик Homebrew, бесчисленные страницы «install.sh». Поддельный сертификат здесь означает запуск кода атакующего, часто от root.
  • CI/CD-пайплайны. Раннеры тянут зависимости и публикуют артефакты, держа облачные учетные данные и ключи подписи, часто на устаревших базовых образах.
  • Трафик между серверами. Вебхуки платежей, обмен токенами OAuth, вызовы облачных SDK с секретами в заголовках.
  • IoT и встраиваемые устройства. Замороженная прошивка с наборами CA, которые не изменятся до истечения сертификатов, то есть до июля 2039 года.
  • Подпись кода для Windows. Два из восьми слабых ключей принадлежали CA для подписи кода; вредоносное ПО, подписанное ими, выглядит как софт легитимного издателя.

Практический совет скучный и правильный: обновите все, что поставляет набор CA. Пакет ca-certificates в вашей ОС, certifi, Node, Homebrew, и пересоберите образы контейнеров, как только базовые образы получат отзыв доверия, потому что слои помнят всё. Неисполнимая часть связана с устройствами, которые вы не можете обновить. Этот умный телевизор будет доверять ключу, равному 4, еще полтора десятилетия. Если вам нужен еще один аргумент за сегментацию сети для IoT-оборудования, вот он.

Миф 5: «Этим наверняка кто-то уже занимается»

Самая тихо убийственная фраза в инциденте принадлежала исследователю: открытие произошло, когда он запустил paranoid_crypto по корпусу CCADB как быструю проверку корпуса, прежде чем перейти к сертификатам абонентов. Низко висящий плод. Слова самого исследователя: «Большую часть времени я потратил на разбор основ, о которых все считали, что кто-то уже позаботился, — критическая ошибка для любой работы рядом с безопасностью».

Перечитайте это еще раз. Восемь приватных ключей CA, драгоценности публично доверенного центра, перекрестно подписанного корнем AAA Certificate Services от Sectigo, семнадцать месяцев были доступны для перебора. Нашел их не аудитор, не корневая программа и не сам CA, а один человек, запустивший инструмент с открытым исходным кодом без настроек. Экосистема коллективно полагала, что основы закрыты. Никто их не закрыл.

Это более широкая закономерность в инфраструктуре безопасности, и поэтому я снова и снова говорю о защитной инженерии — системах, устойчивых к катастрофам, а не системах, которые предполагают правильную работу. Проверки, которые поймали бы это, настолько дешевы, что почти неловко:

  1. Запускайте paranoid_crypto или аналогичные проверки на слабые ключи для каждого ключа при генерации и для каждого CSR перед подписью.
  2. Проверяйте энтропию эмпирически: смотрите битовую длину приватного скаляра и отклоняйте все, у чего эффективная энтропия неправдоподобно мала, как в примере кода выше.
  3. Тестируйте церемонию, а не только конфигурацию. Проводите пробные прогоны церемоний генерации ключей и проверяйте реальные артефакты, включая то, какой именованный объект HSM попадает в CSR.
  4. Непрерывно сканируйте выпущенные сертификаты, а не только шаблоны. Сертификат — это источник истины; всё, что выше по течению, — это намерения.

Сертификат — это источник истины. Всё остальное, то есть скрипты, церемонии и аудиты, — это лишь намерения.

Восьми битов достаточно

Есть что-то почти успокаивающее в катастрофе с нелепой причиной. Ошибка в скрипте, два похожих по имени ключа на HSM, человек выбрал не тот. Никакого государства, никакого нового криптоанализа, просто эпизод из комедии с семнадцатимесячным запалом. Но нелепость и есть предупреждение. Если самое слабое звено публично доверенного CA может оказаться одним байтом, то прочность вашего PKI определяется не выбором кривой и не бюджетом на HSM. Она определяется тем, тестировал ли кто-нибудь в конвейере результат на самом деле.

Производители браузеров сделали свою часть за несколько часов. Остальное за нами: проверьте собственную генерацию ключей, запускайте дешевые проверки, обновите каждый набор CA, до которого можете дотянуться, и сегментируйте устройства, которые не можете обновить. И перестаньте считать, что кто-то другой уже проверил основы. Рядом с безопасностью это самое дорогое допущение из всех возможных.