Почти девять лет назад миру был представлен новый опенсорсный проект под названием NuGet (www.NuGet.org). Спустя два года после своего дебюта NuGet начал поставляться в Microsoft Visual Studio, что актуально и по сей день. NuGet — это один из нескольких пакетных менеджеров (диспетчеров пакетов), таких как Node Package Manager (NPM) для JavaScript и Maven для Java. Пакетные менеджеры упрощают и автоматизируют использование библиотек. Например, если вам нужна библиотека для реализации JavaScript Object Notation (JSON) в вашем .NET-приложении, потребуется всего несколько кликов мышью, и ваше приложение получит мощные возможности, которые вам не нужно реализовывать самим, совершенно бесплатно.
Когда-то разработчики создавали и поддерживали свои собственные библиотеки. Если вам нужна была какая-либо библиотека, скорее всего, вы вежливо спрашивали о ней коллег-разработчиков в онлайн-сообществах где-нибудь на CompuServe, что было принято в подобных сообществах, и были все шансы, что вы могли получить библиотеку, отвечающую вашим потребностям или, по крайней мере, рекомендации, как самому ее создать.
Сегодня программное обеспечение с открытым исходным кодом (Open Source Software - OSS) создало беспрецедентную доступность для систем управления кодом и пакетами, которые делают внедрение этого кода в ваши приложения невероятно легким процессом. Однако, этот прогресс принес не только многочисленные преимущества, но и новые риски и проблемы. Одним из недавних примеров является инцидент с event-stream в ноябре 2018 года, связанный с NPM. Эта статья посвящена тому, как ответственно использовать NuGet в Visual Studio, чтобы снизить подобные риски.
Если вы работаете в публичной компании, регулируемой SOX, или подпадаете под действие HIPAA или PCI, и ваши приложения напрямую зависят от какого-либо публичного NuGet-источника, то есть все шансы, что ваша компания может нарушать вышеупомянутые стандарты, несмотря на отсутствие каких-либо неблагоприятных инцидентов.
Ни одно приложение, работающее в продакшене, или процесс сборки никогда не должны напрямую зависеть от каких-либо пакетов из публичных источников.
Если вы не очень хорошо знакомы с NuGet
В случае, если вы не знакомы с NuGet, не знаете что это такое и как он работает в целом, то я рекомендую вам почитать официальную документацию, где вы можете найти всю интересующую вас информацию. А если вы являетесь подписчиком Pluralsight, вы можете посмотреть мой курс “Введение в NuGet”.
Представленные здесь концепции не требуют глубокого понимания NuGet. Целевая аудитория включает в себя как опытных разработчиков, так и директоров с менеджерами, отвечающих за реализацию политик компании в области безопасности и снижения рисков.
Пакетные менеджеры и источники пакетов
Прежде чем углубляться в базовые концепции пакетных менеджеров в .NET/Visual Studio с NuGet, давайте скажем пару слов о пакетных менеджерах и пакетах в целом. Ниже приведены основные понятия, которые вам нужно знать:
Пакет: Архивный файл (например, zip или tar), который содержит артефакты кода и дополнительные метаданные для пакетного менеджера, которые, в свою очередь, используются средой разработки для добавления содержимого пакета в проект.
Пакетный менеджер: Инструмент, который среда разработки приложений (например, Visual Studio, Eclipse и т. д.) использует для получения доступа к пакетам, содержащимся в источнике пакетов. Распространенными пакетными менеджерами являются NuGet, Maven и Node Package Manager (NPM). Пакетный менеджер берет на себя не только управление доступом к конкретному пакету, он также заботится о доступе к другим пакетам, от которых зависит загруженный пакет (т. е. занимается управлением зависимостями).
Источник пакетов: набор пакетов, где для каждого пакета содержатся метаданные о нем. Эти метаданные включают номер текущей версии, историю релизов, ссылки на репозиторий с исходным кодом (например, на GitHub), документацию, информацию о лицензии. Среди самых популярных источников пакетов можно выделить NuGet.org, MyGet и npmjs.com.
Компаниям следует самим создавать собственные пакеты и заниматься управлением ими и их зависимостями, а также стремиться к созданию и использованию своих собственных внутренних источников пакетов.
Отношения между этими тремя элементами довольно просты: среда разработки приложений использует пакетный менеджер для подключения к источнику пакетов и получения от него пакетов, которые будут использованы в проекте разрабатываемого приложения.
Каковы риски?
Из трех элементов, приведенных в списке выше, риск возникает из-за двух: пакетов и источников пакетов. Источники пакетов, такие как npmjs.com и NuGet.org, являются открытыми до такой степени, что любой желающий может создать учетную запись и загрузить туда пакет, который впоследствии будет загружаться другими. Из-за одной только этой причины такие открытые источники пакетов ненадежны по своей сути. Означает ли это, что вам следует избегать открытых источников? Конечно нет. Это означает, что при получении пакетов из таких источников вы должны проявлять должную осмотрительность, проверяя содержимое этих пакетов. Если вы не можете с уверенностью определить происхождение пакета и его содержимое, вы подвергаете свою фирму риску, которого в противном случае можно было бы избежать. Реальным примером такого риска и его последствий стал инцидент с event-stream, обнаруженный в ноябре 2018 года. Этот инцидент был связан с вредоносным кодом в пакете, который собирал данные об учетных записях со счетов, имеющих баланс биткоинов выше определенного уровня. The Register сообщил, что код являлся частью популярной NPM-библиотеки, которая в среднем загружалась до двух миллионов раз в неделю.
Если вы не можете с уверенностью определить происхождение пакета и его содержимое, вы подвергаете свою фирму риску, которого в противном случае можно было бы избежать.
С одной стороны, открытые источники пакетов делают код легко доступным. С другой стороны, эти открытые источники пакетов НЕ ОСУЩЕСТВЛЯЮТ и, возможно, НЕ МОГУТ В ПРИНЦИПЕ контролировать наличие вредоносного кода в пакетах. Так кто должен следить за этим? Ответ очень прост: ВЫ! Если вы привносите пакет в свою организацию, вы обязаны проверить не только содержимое пакета, но и содержимое всех остальных пакетов, от которых зависит загруженный пакет.
Управление зависимостями — еще одна приятная функция, осуществляемая пакетным менеджером. Если вам показалось, что внедрение вредоносного пакета в вашу организацию похоже на распространение заразного вируса, вы все правильно поняли.
Дело в том, что ни одно приложение, работающее в продакшене, или процесс сборки никогда не должны напрямую зависеть от каких-либо пакетов из публичных источников. Помимо злоумышленников, есть много других менее опасных причин не доверять публичным источникам пакетов:
Вы оставляете за владельцем пакета все, что касается управления зависимостями и версиями. Что, если владелец пакета введет зависимость, которая необходима для работы пакета, но полностью несовместима с вашим приложением?
Что, если владелец пакета загрузит новую версию пакета, которая будет работать, но тем не менее станет причиной бага в вашем приложении? Если вы настроили процесс сборки на автоматическое обновление пакетов, то это может стать причиной дорогостоящей ошибкой, на исправление которой вам придется потратить реальные деньги.
Компаниям следует самим создавать собственные пакеты и заниматься управлением ими и их зависимостями, а также стремиться к созданию и использованию своих собственных внутренних источников пакетов. Если вы используете пакет из публичного источника, вы следует сначала открыть пакет и оценить его содержимое, и только после этого добавлять этот пакет в свой проект или его содержимое в свой собственный пакет.
Разве подписывание пакетов не снижает риск?
Одним словом, да, но это “да” с оговорками. Подписывание снижает некоторые риски, но не все. Подписывание не предотвратило бы инцидент с event-stream. Единственное, что делает подписывание пакета, — это удостоверяет автора/контрибьютора пакета. Безусловно, в большинстве сред вы можете ограничить пакеты, которые вы можете использовать, по определенным авторам. Если у вас есть публичный ключ, то брать можно только те пакеты, которые подписаны авторским сертификатом. Однако это не означает, что вы можете расслабиться и не глядя брать любой пакет от этого автора. Что, если авторский сертификат был скомпрометирован? Что, если автор допустил невинную ошибку, в результате которой ваша компания может понести ущерб?
Теперь, когда у вас есть какое-никакое представление о пакетах, пакетных менеджерах и источниках пакетов, а также о связанных с ними рисках, давайте применим эти знания к NuGet.
Краткий обзор NuGet: создание собственного источника NuGet
Как я уже говорил ранее, эта статья не является подробным практическим руководством по NuGet. Если вас интересует таковое, то вам следует обратиться к материалам, представленным в начале этой статьи. В предыдущем разделе я описывал как взаимосвязаны пакеты, пакетные менеджеры и источники пакетов — NuGet использует тот же подход. Для Visual Studio NuGet является стандартным встроенным пакетным менеджером, и найти его можно как показано на рисунке 1.
Если вы используете пакет из публичного источника, вам следует сначала открыть пакет и оценить его содержимое, и только после этого добавлять этот пакет в свой проект или его содержимое в свой собственный пакет.
Также на рисунке 1 показан источник пакета. Скорее всего, вашим активным источником пакетов является NuGet.org. В моем случае это нечто с именем Local Package Source. Рисунок 2 иллюстрирует, что это такое:
Как видите, локальный источник NuGet — это просто каталог на моем рабочем компьютере. Это может слегка шокировать, но создать источник NuGet так же просто, как создать каталог! На рисунке 3 показаны пакеты NuGet в этом каталоге:
Анатомия пакета NuGet
Пакет NuGet — это просто ZIP-архив с другим расширением (.nupkg). На рисунке 4 показано, как открыть его содержимое.
На рисунке 5 показано содержимое пакета. Давайте посмотрим, что находится внутри одного из самых популярных и широко используемых пакетов NuGet: NewtonSoft.Json.
Глядя на рисунок 5, представляющие особый интерес элементы — это папка lib и файлы подписи, лицензии и nuspec:
Папка lib: эта папка содержит одну или несколько вложенных папок, названных в соответствии с соглашением об именовании для каждой поддерживаемой версии .NET. Вы можете узнать больше о поддержке сразу нескольких версий .NET здесь.
Файл .signature.p7s: как следует из названия, это файл подписи, подписанный авторским сертификатом. Дополнительную информацию о том, как подписывать пакеты NuGet, можно найти здесь. Вы также можете узнать, как требовать, чтобы были доступны только подписанные пакеты, и ограничить пакеты определенными авторами здесь.
License.md: это разметочный файл, содержащий условия лицензии для вашего пакета. Как правило, это лицензия с открытым исходным кодом, такая как MIT, GNU или Apache 2.0.
Nuspec: Nuspec является файлом-манифестом. Это XML-файл, который используется для создания пакета NuGet. Про этот файл мы поговорим в следующем разделе.
Создание собственного пакета NuGet
Теперь вы понимаете, что такое пакеты, пакетные менеджеры и источники пакетов, и имеете общее представление о том, как NuGet вписывается в это пространство. Вы также понимаете, как создавать и ссылаться на свой собственный источник пакетов, используя не что иное, как простейшую паку с файлам. Осталось только научиться создавать собственный пакет NuGet. Чтобы проиллюстрировать это, я собираюсь использовать свою библиотеку неизменяемых классов, о которой я уже писал в прошлом.
Существует несколько подходов, которые можно использовать для создания пакетов NuGet. Я собираюсь показать вам метод, который я считаю самым понятным и простым в использовании. Есть также много других вариантов, которые вы можете применить, но я не буду здесь их описывать. Для полного охвата всего, что вы можете делать в рамках создания пакетов, обратитесь к документации на NuGet.org.
Шаг 1: Создайте структуру каталогов пакета (Package Directory Structure) и добавьте свои бинарники
Рисунок 6 иллюстрирует структуру каталогов.
Я добавил icon.png, который будет отображаться в пакетном менеджере, как показано на рисунке 1. Текстовый файл License содержит лицензию MIT. И, наконец, nuspec, показанный на рисунке 7:
Шаг 2: Создайте файл Nuspec
Файл nuspec, показанный на рисунке 7, очень прост.
Полную справку по nuspec можно найти здесь. Идентификатор (ID), который вы выбираете для своего пакета, должен быть уникальным в контексте источника, в котором он размещен. Соответственно, если вы решите сделать свой пакет NuGet доступным на NuGet.org, то идентификатор должен быть уникальным для этого источника. На рисунке 8 показано, как пакет отображается в пакетном менеджере NuGet:
Шаг 3: Создайте пакет NuGet
Чтобы создать пакет NuGet из командной строки, вам потребуются инструменты командной строки NuGet. На рисунке 9 показано, где можно скачать NuGet.exe.
На рисунке 10 показано, как создать пакет NuGet:
Шаг 4: Опубликуйте свой пакет
В зависимости от типа используемого источника пакетов ваши действия могут немного отличаться. Для источника-каталога процесс заключается в копировании файла в каталог. Если вы хостите свой собственный сервер NuGet, вам придется использовать один из методов, описанных здесь.
Другие варианты хостинга
Вместо самостоятельного хостинга или использования публичного NuGet.org вы можете выбрать стороннюю службу. Для NuGet существуют платные сервисы, такие как myget (myget.org) и Chocolatey (chocolatey.org). Если свой собственный источник так просто создать, то зачем вам может понадобиться платный сервис? Эти платные сервисы имеют собственную инфраструктуру аварийного восстановления (Disaster Recovery). Если вы хостите свой собственный источник, вам необходимо думать о том, как будет выполняться резервное копирование и репликация вашего сервера, а также как вы будете восстанавливаться в случае какого-либо катастрофического события.
Заключение
Опенсорс упростил добавление фич в ваши приложения. Частью этой простоты является скорость. Скорость и простота означают меньше “трения” (friction). Давным-давно, до опенсорса, каким мы его знаем сегодня, до интернета и до управления пакетами, в системе существовало неявное трение, которое давало нам время для оценки и анализа. Разработчики прошлого поколения, на мой взгляд, лучше разбирались в управлении изменениями. Они осознавали дисциплину и строгость, необходимые для снижения риска. При всех преимуществах современных технологий, а также скорости и простоте, которые мы получаем с ними, как никогда важно использовать методы снижения рисков, такие как те, что мы обсуждали в этой статье, потому что, если нам легче делать хорошие вещи, то также и злоумышленникам легче проворачивать их грязные делишки. Надежная защита и снижение рисков не бесплатны. Один из самых коварных негативных побочных эффектов бесплатного опенсорса — это ожидание того, что вещи, которые до сих пор имели цену, больше не имеют никакой цены. Учитывайте это в следующий раз, когда будете вводить пакет в вашу среду. Если ваша организация регулируется SOX, HIPAA, FINRA, PCI и т. д., и вы соответствуете этим требованиям, то вы не допустите такой ситуации.
Сегодня вечером в OTUS состоится открытый урок «Магические слова async / await», на котором разберем механизм, скрытый под ключевыми словами async/await. Также рассмотрим правильное использование этих ключевых слов и некоторые другие аспекты асинхронного программирования на C#. Регистрация по ссылке.