Как убедиться, что код библиотеки не был изменен?
Любые Open Source проекты потенциально подвержены риску вредоносной модификации сторонними контрибьюторами и уязвимость может появиться на любом уровне распространения кода — от исходных текстов до двоичных артефактов, которые связываются с прикладными программами. Кроме того, нередко в библиотеках и подсистемах Linux обнаруживаются ошибки, которые могут привести к нарушению функционирования системы, аварийному завершению приложения или обходу механизмов безопасности и важно иметь возможность быстро обнаружить все затронутые этой ошибки компоненты и выполнить выпуск заплатки для 0-day уязвимости.
Для решения всех этих задач Linux Foundation в 2010 году предложил формат для описания юридических аспектов использования программного обеспечения (в частности, лицензии), происхождения артефакта (исходного кода или двоичного файла), способов контроля целостности объекта, а также зависимостей от других объектов. В 2021 году формат был принят как стандарт ISO/IEC 5962:2021 (спецификация SPVX V2.2.1) и является основой для определения SBOM (Software Bill of Materials), которые описывают зависимости компонентов, способы их верификации и описание цепочки поставок для валидации источника. В статье мы обсудим общие принципы описания SPDX и инструменты (включая недавно открытый Microsoft SBOM Tool, ранее назывался Salus), для создания SPDX/SBOM (в том числе, для автоматической генерации на основании файлов спецификации зависимостей проекта).
Начнем мы с одного из самых крупных Open Source проектов с большим количеством контрибьюторов — ядра Linux. Ядро состоит из многих подсистем и может быть собрано на разные аппаратные архитектуры, процесс сборки регулируется набором флагов, которые влияют на разрешенные для сборки компоненты или отдельные функциональные возможности. Поскольку ядро распространяется с открытым исходным кодом, крайне важным становится корректное использование подключаемых заголовочных файлов или использование исходных текстов для создания производных продуктов (например, разработки драйвера для новой файловой системы). Для возможности автоматической проверки корректности использования исходного кода подсистемы ядра, во большинство файлов с исходными текстами ядра был добавлен SPDX-заголовок (в первую строку с комментарием) в текстовом представлении SPDX-License-Identifier: <идентификатор_лицензии>. Идентификатор может быть комбинированным (через AND или OR) и собирается из зарегистрированных ID, которые можно посмотреть на сайте.
Метаданные SPDX могут включать в себя кроме лицензии следующие атрибуты (согласно спецификации):
SPDXVersion
— версия спецификации;SPDXID
— уникальный идентификатор артефакта;DocumentName
— название проекта;DocumentNamespace
— пространство имен организации, ответственной за проект (URI);Creator: Person
— имя и адрес электронной почты автора;Creator: Organization
— организация, которая ответственна за создание проекта;Creator: Tool
— название инструмента, которым был созданы SPDX;PackageName
— название пакета;PackageVersion
— версия пакета;PackageSupplier
— автор пакета (Person, Organization);PackageFileName
— название файла с пакетом (архив или каталог на диске);PackageDownloadLocation
— адрес для получения пакета (например, путь к каталогу в github-репозитории);PackageCopyrightText
— текст лицензии;PackageLicenseDeclared
— лицензия пакета;PackageLicenseInfoFromFiles
— лицензия, полученная из исходных текстов;PackageHomePage
— домашняя страница проекта;PackageSourceInfo
— дополнительные сведения об исходных текстах или сборке проекта;PackageVerificationCode
— SHA1-подпись пакета в целом (архива, из которого исключаются файлы spdx);PackageChecksum
— хэш пакета (SHA1, SHA256, MD5);PackageDescription
— описание пакета;ExternalRef
— связь с внешними идентификаторами (например, SECURITY cpe);ExternalRefComment
— текстовое описание внешнего отношения;FileAnalyzed
— логическое значение, true, если файлы пакета были проанализированы и для них рассчитаны хэши (для проверки целостности);Relationship
— отношения между SPDXID (например, может быть SPDXRef-DOCUMENT DESCRIBES SPDXRef-Package-hello). Отношения могут включать:DESCRIBES
— первый объект SPDX описывает второй;GENERATED_FROM
— первый объект SPDX создан из второго;BUILD_TOOL_OF
— первый объект является инструментом (или описанием) сборки второго;CONTAINS
— первый объект содержит второй;DEPENDS_ON
— первый объект зависит от второго;DEPENDENCY_OF
— первый объект является зависимостью второго;EXAMPLE_OF
— первый объект пример использования второго;GENERATES
— первый объект создает (или определяет правила сборка) для второго;… (полный список можно посмотреть в документации).
FileName
— название файла, для которого рассчитывается хэш;FileType
— тип файла (например SOURCE или BINARY, а также DOCUMENTATION для файлов документации, AUDIO/IMAGE/VIDEO для мультимедиа, TEXT — текстовый файл, APPLICATION — все файлы с MIME application/* (например, JSON), ARCHIVE для архивов, а также SPDX для описания самого SPDX-файла);Filechecksum
: SHA1 — хэш файла в SHA1;Filechecksum
: SHA256 — хэш файла в SHA256;Filechecksum
: MD5 — хэш файла в MD5;LicenseInfoInFile
— информация о лицензии в файле;LicenseComments
— произвольные пояснения к лицензии;FileCopyrightText
— текст лицензии из файла;FileComment
— произвольный комментарий к файлу;
Также могут определяться сниппеты (внутри ранее описанных файлов с указанием диапазона байт или строк, где он расположен), при этом для сниппета может быть задана своя лицензия.
Для любого атрибута может быть указано NOASSERTION, это подразумевает, что значение не получилось определить и оно будет игнорироваться при валидации.
SPDX может быть представлен в исходных файлах (в комментариях), в виде YAML-документа, в JSON, XLS, RDF, а также простым текстовым файлом (строки вида ключ: значение), также планируется XML.
Поскольку SPDX-файл добавляется в github-репозиторий проекта и может быть получен отдельно от архива с исходным текстом, можно быть уверенным, что исходные файлы и двоичные артефакты не были изменены. Но все же создавать файл вручную достаточно трудоемкая задача, кроме того в проекте могут быть внешние зависимости. Поэтому в спецификации предусмотрена возможность автоматической генерации, в том числе с определением зависимостей из конфигурации Apache Maven, npm-package.json, NuGet, RDF, Bower. Кроме того, генераторы могут добавить свои распознаватели для поддержки других систем описания зависимостей.
Для проверки возможностей генерации и проверки сохранности артефактов создадим пустой проект для Rust:
cargo init
cargo add math
cargo update
И установим spdx-sbom-generator из релизных сборок из https://github.com/opensbom-generator/spdx-sbom-generator. Генератор поддерживает разбор файлов описания зависимостей для Go (GoMod), Rust (Cargo), PHP (Composer), .NET, Java (Maven), Node.js (npm, yarn), Python (PIP, Pipenv), Ruby (Gems) и Swift (Swift Package Manager).
После этого в каталоге проекта мы можем запустить генератор и получим bom-cargo.spdx, в котором будут описываться как само приложение, так и пакеты, от которых приложение зависит (прямо или транзитивно), например:
SPDXVersion: SPDX-2.2
DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT
DocumentName: rt-0.1.0
DocumentNamespace: http://spdx.org/spdxpackages/rt-0.1.0-ebdc4335-b6ae-404b-a737-59867065a59a
Creator: Tool: spdx-sbom-generator-v0.0.15
Created: 2022-07-13T22:24:36Z
PackageName: rt
SPDXID: SPDXRef-Package-rt
PackageVersion: 0.1.0
PackageSupplier: Organization: rt
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
PackageChecksum: SHA1: 5f5d733b1d526d8bccf659d08ecc1e1aa5ea18a6
PackageName: autocfg
SPDXID: SPDXRef-Package-autocfg-1.1.0
PackageVersion: 1.1.0
PackageSupplier: Person: Josh Stone (cuviper@gmail.com)
PackageDownloadLocation: https://github.com/cuviper/autocfg
FilesAnalyzed: false
PackageChecksum: SHA1: 70d7af357d0d83f1cb6d1b04b215e7b6acab62c8
PackageHomePage: https://github.com/rust-lang/crates.io-index
PackageName: math
SPDXID: SPDXRef-Package-math-0.10.0
PackageVersion: 0.10.0
PackageSupplier: Organization: Aaron Zorel
PackageDownloadLocation: https://github.com/Rustinante/math
FilesAnalyzed: false
PackageChecksum: SHA1: 2126997e3be1c9f16826795af71bd8cb3ef6de92
PackageHomePage: https://github.com/rust-lang/crates.io-index
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-Package-rt
Relationship: SPDXRef-Package-rt DEPENDS_ON SPDXRef-Package-math-0.10.0
Relationship: SPDXRef-Package-math-0.10.0 DEPENDS_ON SPDXRef-Package-log-0.4.17
Relationship: SPDXRef-Package-math-0.10.0 DEPENDS_ON SPDXRef-Package-num-0.2.1
Relationship: SPDXRef-Package-math-0.10.0 DEPENDS_ON SPDXRef-Package-rand-0.7.3
Relationship: SPDXRef-Package-math-0.10.0 DEPENDS_ON SPDXRef-Package-rayon-1.5.3
Для файлов хэши не рассчитываются.
Для проверки корректности зависимостей можно использовать онлайн-инструмент для валидации. Также на tools.spdx.org можно выполнять преобразование между различными форматами представления SPDX.
Второй инструмент, который поддерживает больший набор библиотек для анализа (к ранее обозначенным добавляются rpm, dpkg, Dart (pub), C, C++, Objective C (Cocoapods), Python Wheel) — это syft. Он также может быть установлен из релиза или собран из исходных текстов. Syft создает SBOM для Docker-образов или файловой системы, а также может выполнять проверку валидности образа.
Создадим описание нашего проекта:
syft packages . -o spdx-tag-value --file rt.spdx
Результатом будет текстовый файл с key-value парами (также может быть создан spdx-json) с перечислением зависимостей проекта. Контрольные суммы здесь не указываются, но есть ссылки на идентификаторы обнаружения уязвимостей CPE.
Еще один инструмент для создания SPDX, который использует наработки проекта ORT (Open Source Software Review Toolkit) — https://github.com/philips-software/spdx-builder. В целом он работает аналогично рассмотренным выше.
Последним инструментом, который мы сегодня рассмотрим будет недавно открытый Microsoft SBOM Tool (ранее Salus). После установки (из исходных текстов или исполняемым файлом) может запустить инструмент в режиме генерации SBOM:
sbom-tool generate -b . -bc . -pn rt -pv 1.0.0 -nsb example.com
Здесь -b
указывает на каталог, где будет создан манифест (в подкаталоге _manifest/spdx2.2), -bc
на каталог с исходными текстами проекта, -pn
— название пакета, -pv
— версия пакета и -nsb
— префикс пространства имен). В результате будет создан файл _manifest/spdx2.2/manifest.spdx.json (по умолчанию, может быть переопределен), который содержит информацию о хэшах файлов проекта (SHA256 и SHA1), а также об обнаруженных зависимостях. Для проверки корректности нужно использовать режим validate:
sbom-tool validate -b . -o /tmp -mi spdx-json:2.2
Как и другие инструменты, Microsoft SBOM Tool может использоваться для сканирования Docker-образов (sbom-tool generate -di ubuntu:latest
). Список поддерживаемых менеджеров зависимостей почти совпадает с syft, но включает еще Ivy и публичные Github-репозитории.
Нужно отметить, что кроме SPDX для представления SBOM также используются CycloneDX и SWID и многие инструменты позволяют экспортировать данные в один из трех форматов.
Проверка корректности зависимостей и собственных исходных кодов может быть выполнена в конвейере CI/CD, например для многих решений есть подготовленный репозиторий для Github Actions. Включение проверки SBOM значительно увеличивает шансы обнаружения неожиданных изменений в зависимых библиотеках (или образах контейнеров) и помогает снизить вероятность попадания вредоносного кода.
Сегодня вечером в OTUS состоится открытое занятие »SSH. Возможности утилиты безопасной оболочки», на котором мы поговорим про:
— Утилиту SSH, как появилась и почему до сих пор популярна.
— Повышение безопасности подключения по SSH.
— Неочевидные методы работы с SSH.
— Безопасную передачу файлов по сети с использованием ssh и утилиты rsync.
Регистрация доступна по ссылке для всех желающих.