Привет!
Меня зовут Руслан Качмазов, я стажёр-инженер Yandex Cloud Security, занимаюсь детектированием инцидентов. Вместе с коллегами Алибеком Епхиевым и Дмитрием Пикушем, выпускниками Школы ИБ Яндекса 2023 года, мы расскажем об атаках на цепочки поставок и методах защиты от них, которые реализованы в группе проектов Sigstore. Поговорим о SLSA, Cosign, Fulcio и Rekor — методологии и инструментах, позволяющих снизить риск таких атак, их гарантиях и тонких местах.
Материал будет полезен для команд и проектов, которые имеют множество внешних зависимостей, но в то же время желают обезопасить себя от атак на цепочки поставок.
В чём суть атак типа supply chain
В мире IT всё переплетается: часто использование одной технологии в проекте ведёт к подключению десятка сторонних библиотек. С одной стороны, замечательно, что можно не изобретать велосипед и использовать уже готовые решения, но с другой — оказывается, что проект становится уязвимее: если злоумышленникам удастся внедрить вредонос в зависимые библиотеки, у них есть шанс получить контроль над всей цепочкой поставки ПО и навредить проекту в целом.
Это и есть supply chain attack, или атака на цепочку поставок — вредонос в зависимостях, который может угрожать непосредственно нашему проекту.
За примером атаки далеко ходить не надо: в апреле 2022-го такой инцидент выявила группа безопасности GitHub. В ходе атаки пострадали не менее 12 клиентов GitHub, которые использовали приложения OAuth от сторонних интеграторов Heroku и Travis-CI. Злоумышленники украли выданные этими поставщиками пользовательские токены OAuth и использовали их для загрузки данных от десятков организаций — инцидент в том числе затронул проект NPM и саму службу размещения репозитория.
В ответ на проблему появилась методология Supply-chain Levels for Software Artifacts — сокращённо SLSA, или salsa.
Что предлагает методология SLSA
SLSA — это набор техник и рекомендаций, созданный для контроля цепочки поставок, повышения целостности и осведомленности об инфраструктуре проекта. Проект salsa поддерживают крупные IT-компании и сообщества, например, The Linux Foundation.
Прежде чем использовать ту или иную зависимость, мы должны убедиться, что она действительно исходит от доверенного источника. Для того чтобы считаться доверенным, источник должен подтвердить, что его библиотека отвечает всем заявленным требованиям и не содержит в себе каких-либо полезных нагрузок, которые могли бы навредить тому, кто ей пользуется. В идеале у нас должен быть т.н. provenance — дополнительная информация о происхождении артефакта, по которой можно с самого начала отследить, кем, где, когда и как что-то было создано.
Чтобы следовать этому принципу, SLSA вводит 4 уровня защищённости, точнее, технически их пять: от полного отсутствия каких-либо гарантий до близкого к идеальному четвёртого уровня.
Уровень | Краткое описание в версии 0.1 |
0 | Никаких гарантий. SLSA 0 означает отсутствие какого-либо уровня SLSA |
1 | Процесс сборки задокументирован |
2 | Сервис сборки устойчив к взломам |
3 | Дополнительная устойчивость к конкретным угрозам |
4 | Высочайший уровень доверия |
Каждый уровень содержит требования к источнику кода (Source), процессу сборки (Build), а также к дополнительной информации о происхождении артефактов (Рrovenance). Подробнее об этом можно прочитать в версии спецификации v.0.1 (обновленная v.1.0 пока не содержит полной таблицы требований).
В контексте статьи не будем подробно останавливаться на всех требованиях, а сосредоточимся именно на provenance, который и позволяет следить за всей цепочкой поставки. Из детального анализа спецификаций видим, что требования для защиты выше первого уровня содержат доказательство, которое в терминологии SLSA называется authenticated provenance — когда пользователь может проверить подлинность и целостность артефакта, например, благодаря подписи (или аналогичной технологии). На третьем уровне к этим требованиям добавляется защита от компрометации секретов: ключей, сертификатов, токенов и т.д. — всего, что используется для такого подтверждения подлинности кода/сборки.
Так что перейдём к конкретным инструментам, которые помогают выполнить эти требования. Sigstore позволяет нам подписывать файлы, контейнеры, blob'ы, манифесты и всё такое прочее. Отмечу, что он имеет третий уровень защиты по шкале SLSA.
Напоследок добавлю, что SLSA — не единственная в своем роде методология. Есть, например, аналог — SCVS, о котором можно почитать на сайте OWASP.
Sigstore — реализация техник SLSA
Проект Sigstore появился в середине 2020 года в ответ на попытки найти технологию для подписи и верификации кода. Запустил проект Люк Хайндс, член security response team в k8s и избранный участник Open Source Secure Foundation (OSSF), при участии Red Hat в качестве компании-основателя. 9 марта 2021 года Sigstore стал проектом The Linux Foundation, но при этом сохранил ссылку на членов-основателей, в число которых входят Red Hat, Google и Университет Пердью. 25 октября 2022 года Sigstore был отмечен как общедоступный, поскольку он объявил о доступности компонент Rekor и Fulcio.
Пройдёмся по технологиям, которые используются в проекте:
OpenID Connect (OIDC) — известный протокол аутентификации, позволяющий получать информацию о пользователе от стороннего IDP (identity provider).
Fulcio — бесплатный CA (Certificate Authority), созданный для того, чтобы сделать сертификаты с коротким сроком действия доступными любому пользователю. Fulcio использует адрес электронной почты OpenID Connect для подтверждения личности пользователя и подписывает сертификаты X.509, действительные в течение 10 минут.
Rekor — журнал прозрачности, основанный на концепции verifiable data structures, то есть журнал, в котором регистрируется информация о выпущенных сертификатах. Такой журнал использует структуру хэшей, известную как дерево Меркла, и позволяет лишь добавление новых записей. Если какая-либо из них будет отредактирована или удалена, то это заметят сторонние аудиторы. Записи представляют из себя метаданные, с помощью которых можно проверить факт подписи и время, в которое эта подпись была сделана.
Эфемерный ключ — криптографический ключ, созданный специально для выполнения только одного действия (в данном случае — подписи сертификата), после чего он исчезает. В Sigstore эфемерный ключ на время подписания хранится в оперативной памяти пользователя, он никуда не передаётся, поэтому его не смогут получить даже те, кто курирует центры сертификации.
Sigstore поддерживает как классическую подпись с открытым и закрытым ключом, так и keyless-подпись. В ранних версиях Sigstore основным методом подписи были именно public/private keys, но 23 февраля 2023 вышел Sigstore 2.0, где основным стал keyless, которым, по заявлению разработчиков, можно пользоваться в продакшн. Этим удобен Sigstore — он позволяет сгенерировать краткосрочный сертификат, через OIDC, где самим этим сертификатом и вшитым в него ID пользователя будет подтвержден факт подписи.
В Sigstore реализованы такие механизмы подписи, как cosign, sigstore-python, sigstore-java и некоторые другие, но в этой статье мы будем рассматривать именно keyless-подпись и механизм Cosign.
Как работает keyless-подпись в Sigstore
Пошагово пройдёмся по алгоритму подписи и уделим особое внимание тому, как подтверждается авторство подписи и гарантируется подлинность выданного сертификата.
Генерируем запрос на сертификат. По команде sign в Fulcio отправляется запрос, в котором содержится OIDC-токен, открытый ключ и подписанный запрос. Вместо открытого ключа и подписанного запроса клиент может передать запрос на подписание сертификата (CSR), который также предоставляет подтверждение владения и открытый ключ.
Проверяем, что пользователь — тот, за кого себя выдаёт. Пользователь проходит аутентификацию и подтверждает свою личность на GitHub, Google или Microsoft.
Проверяем фактор владения. После того как пользователь прошел аутентификацию, проверяется, владеет ли пользователь закрытым ключом своего открытого ключа — для этого Fulcio проверяет подписанный запрос или CSR. Для подписанного запроса это подпись subject (sub).
Формируем предварительный сертификат. Пользователь аутентифицирован и подтвердил владение закрытым ключом. Fulcio должен создать сертификат для подписи кода, который удостоверит личность подписанта из токена идентификатора. На этом этапе создаётся расширение сертификата X.509.
Формируем цепочку доверия от выданного сертификата до корневого сертификата CA. Для формирования цепочки доверия заполненный сертификат для подписи кода должен быть подписан центром сертификации. Fulcio поддерживает такие серверные системы для подписи сертификатов, как: KMS, Tink, PKCS#11, CA Google, файлы (для закрытых ключей с паролем) и эфемерный, для тестов.
Добавляем подписанный сертификат в CT — неизменяемый, предназначенный только для добавления, криптографически проверяемый журнал прозрачности сертификата. Это позволит публично проверять выпуск. Для этого нам нужно сопроводить сертификат необходимыми метаданными: временными метками, которые подтверждают факт подписи.
Что важно: CT Fulcio и Rekor, хоть и похожи идейно, всё же разные вещи: первый хранит только выданные сертификаты, в то время как Rekor — подписи артефактов и подтверждения.
На этом этапе в сертификат включается созданное расширение X.509.
Затем журнал прозрачности сертификата возвращает временную метку подписанного сертификата (SCT, или Signed Certificate Timestamp). SCT встраивается в сертификат и снова подписывается.
Возвращаем сертификат пользователю.
Как эти механизмы используются в Cosign
Сosign — инструмент Sigstore для подписи артефактов кода. Из названия можно подумать, что он может подписывать только контейнеры (изначально так и было), но Сosign также не брезгует blob-файлами. В этом обзоре на blob останавливаться не будем, поскольку команды и процедура их подписи практически идентичны контейнерам.
Давайте посмотрим, как работает описанный механизм подписи при верификации контейнеров, и что к нему добавляется в процессе.
Допустим, в нашем репозитории докера лежит некоторый образ, который мы хотим подписать:
$ cosign sign <IMAGE URI>
По умолчанию используется keyless-подпись.
После того как мы ввели команду sign, мы проходим авторизацию у поставщика удостоверений OIDC, чтобы получить токен идентификатора OIDC.
Это выглядит так, что Sigstore просит нас авторизироваться в GitHub, Google или Microsoft и таким образом подтвердить, что мы те, за кого себя выдаём.Затем в Fulcio отправляется запрос со сгенерированным открытым ключом и токеном OIDC, где проверяется подлинность этого токена у поставщика удостоверений OIDC. Если подлинность подтвердилась, то Fulcio извлекает субъекта из токена для использования в качестве SAN в сертификате.
Fulcio генерирует и подписывает предварительный сертификат, который загружает в свой журнал certificate transparency (CT). Журнал генерирует и подписывает SCT (временную метку сертификата), которая отправляется обратно в центр сертификации Fulcio. Затем он объединяет SCT с предварительным сертификатом для получения окончательного сертификата, который отправляется пользователю, подписавшему контейнер.
Пользователь использует ключ, связанный с сертификатом, для подписи артефакта. И вот теперь загружает сертификат и подпись в Rekor, который сохраняет их в журналах и возвращает временную метку подписанной записи, гарантирующую включение в журнал в определённое время. Верификатор может использовать это время, чтобы убедиться, что сертификат использовался, пока он был действительным.
Как после этого проверить подлинность? Для верификации подписанного образа также используется простая команда:
$ cosign verify <IMAGE URI> --certificate-identity=name@example.com
--certificate-oidc-issuer=https://accounts.example.com
Когда мы вводим команду verify, то cosign проверяет Fulcio CT и Rekor на предмет того, является ли субъектом name@example.com и подписан ли сертификат в заявленное время.
Проверить подпись можно и вручную: вместе с артефактом мы получаем подпись и сертификат Fulcio. Мы можем заглянуть в Fulcio CT и проверять наличие сертификата, после чего пойти в Rekor и убедиться, что подпись была осуществлена в период действия сертификата, запросив подтверждение через инструменты rekor-cli или rekor-ui.
Какие гарантии Sigstore даёт, а какие — нет
Sigstore — довольно молодой проект, который соответствует современным стандартам безопасности и находится в стадии постоянного улучшения. Например, как я уже упоминал, keyless-подпись ещё недавно была лишь экспериментальной, а сейчас стала основной.
Разработчики стараются дать нам качественный продукт с открытым исходным кодом, которым могут пользоваться все. Но какие конкретно гарантии дает Sigstore?
Из их официальной документации:
Sigstore’s trust model originates from the Trust Root and chains down to short-lived certificates issued by Fulcio
Мы уже разобрались, как выпускаются краткосрочные сертификаты от Fulcio и как с их помощью проверить подлинность подписи на момент выпуска сертификата. Но как проверить доверие ко всему репозиторию, и как в этом помогает Trust Root? Здесь используются принципы TUF — фреймворка для защиты систем программного обеспечения, который основан на моделях угроз, специфичных для атак на цепочки поставок. Trust Root состоит из 5 ротируемых держателей ключей из разных компаний и учреждений и, следуя протоколам TUF, представляет из себя гарантию надежности.
Таким образом, Sigstore Trust Root применяется для защиты ключей, используемых всем проектом Sigstore. Это позволяет отдельным лицам и системам автоматически извлекать доверенные ключи и сертификаты, которые используются для проверки артефактов, созданных экосистемой Sigstore. С использованием Sigstore Trust Root пользователи могут проверить:
сертификаты, выданные Fulcio;
записи в журнале прозрачности Rekor.
Это помогает конечным пользователям убедиться, что распространители используемого ими ПО являются теми, за кого себя выдают.
Что Sigstore не гарантирует:
Если удостоверение OIDC или поставщик (например, GitHub или его ответы по протоколу OIDC) скомпрометированы, Fulcio может выдать неавторизованные сертификаты. Однако эти сертификаты бесполезны, если они не опубликованы в журнале прозрачности сертификатов, поэтому такая компрометация должна быть обнаружена при проверке журнала.
Если Fulcio скомпрометирован, он может выдать неавторизованные сертификаты. Однако, как и прежде, они должны быть обнаружены в процессе верификации.
Соответственно, если никакие третьи лица не отслеживают журналы, любое неправомерное поведение Rekor и Fulcio может остаться незамеченным.
Как видно из документации, Sigstore использует под капотом сложный механизм: цветом на схеме выделены те места, при компрометации которых можно подделать подпись.
Есть ещё один тонкий момент, связанный с Rekor, краткосрочными сертификатами и метками времени, опять же, из документации самого Sigstore:
Transparency Logs make it hard to forge timestamps long term, but in short time windows, it would be much easier for the Rekor operator to fake or forge timestamps.
To mitigate this, Rekor’s timestamps and tree head are signed with a valid Signed Tree Head (STH) that contains a non-repudiable timestamp. These signed timestamp tokens are saved as evidence in case Rekor’s clock changes in the future.
То есть, в пределах 10-минутного окна риск подделки временной метки нельзя исключить совсем. Для того чтобы минимизировать этот риск, время в журнале Rekor подписывается с использованием заявления STH, в котором содержится отдельная неопровержимая временная метка. Если допустить, что Rekor постоянно находится под наблюдением третьих лиц, то вероятность редактирования временной метки для уже существующей записи или добавления новой метки задним числом сводится к нулю — мы всегда сможем обнаружить следы подмены. Для этого и нужны аудиторы.
Именно здесь может произойти TOCTOU — тип атаки, похожий на race conditions. Механизм атаки заключается в том, что в промежуток между подтверждением пользователем своей личности с помощью OIDC и установкой временной метки можно изменить артефакт.
Представим себе ситуацию, что артефакт в виде образа был загружен во внешний registry, где человек, имеющий доступ ко всем образам этого registry (хозяин или злоумышленник), может изменять образы. Тогда мы попадаем в ситуацию, где:
пользователь начал процесс подписи образа, который находится во внешнем registry, подтвердил свою личность через OIDC;
в момент между подтверждением личности и установкой временной метки образ изменяется;
сертификат получает временную метку и считается валидным.
При загрузке нового образа от автора изменения злоумышленника будут утеряны.
Более подробное описание гарантий, моделей угроз и безопасности от Sigstore можно найти на двух страницах документации: Модель безопасности и Модель угроз.
Также, если вы планируете внедрять Sigstore в свои проекты, то настоятельно рекомендую прочесть небольшой документ, где раскрыты все виды возможных уязвимостей, а процессы описаны чётким формальным языком.
Как использовать эти инструменты в Yandex Cloud
В Yandex Cloud есть возможность использовать Cosign для подписи артефактов: образов и in-to-to аттестаций. Для того чтобы добиться соответствия разрабатываемого софта остальным требованиям SLSA, мы рекомендуем клиентам облака использовать Cosign вместе с другими инструментами. Кратко пройдёмся по каждому уровню и посмотрим, какие механизмы можно добавить для снижения рисков.
Уровень 1 и 2 покрывается за счёт встроенных возможностей Managed Service for GitLab, особенно это касается генерации provenance attestation.
Подробнее можно узнать в статье GitLab о соответствии до 2 уровня SLSA включительно: Achieve SLSA Level 2 compliance with GitLab.
Уровень 3 достигается с использованием решения Сosign для подписи артефактов и дальнейшей загрузкой их в Yandex Container Registry.
Есть отдельная инструкция о том, как можно подписывать и проверять подпись в Managed Service for Kubernetes с помощью Cosign и kyverno. Ключ подписи необходимо хранить отдельно от пайплайна — в этом помогает сервис Yandex Lockbox, который поддерживает шифрование с помощью облачных ключей KMS и имеет интеграцию с Managed Service for Kubernetes.
Уровень 4: система должна соответствовать стандартам безопасности и обладать свойствами герметичности и изолированности.
Помимо того, что само облако соответствует стандартам безопасности, как отечественным, так и международным, на сайте также можно ознакомиться с собственным Стандартом по защите облачной инфраструктуры Yandex Cloud 1.0 и отдельными рекомендациями о безопасности CI/CD-системы на базе управляемого GitLab.
У пользователей есть возможность проводить сканирование на уязвимости своих образов, хранящихся в Yandex Container Registry с помощью сканера уязвимостей. Сканировать можно автоматически при загрузке либо встроить в пайплайн по примерам.
Если вы хотите на практике ближе познакомиться с методологией DevSecOps, расширить ваши CI/CD‑пайплайны специализированными сканерами и анализаторами, чтобы обезопасить свои приложения, поможет бесплатный курс DevSecOps в облачном CI/CD от Yandex Cloud.
Общие впечатления
Важно понимать, что сам по себе Sigstore не защитит от атак на цепочки поставок, инструмент будет работать тогда, когда компания сможет убедить всех своих поставщиков подписывать продукты с его помощью. Если удалось договориться о систематической подписи поставляемого ПО, с созданной для этого инфраструктурой, — даже несмотря на некоторые вопросы к Sigstore в плане безопасности, внедрение таких правил если не полностью защитит от supply-chain-атак, то значительно снизит риск их возникновения.