Атаки на цепочку поставок во фронтенде - Attacking Downstream from Upstream
Last updated
Last updated
Атаки на цепочки поставок направлены на уязвимости в Upstream
. Как только Upstream
скомпрометирован, Downstream
также будет заражен.
Если взять в качестве примера фронтенд, то используемые вами пакеты npm или сторонние скрипты, которые вы импортируете в свой код, считаются Upstream
. Вы когда-нибудь знали о рисках, связанных с использованием этих сторонних ресурсов?
В этой статье на примере cdnjs мы рассмотрим атаки на цепочку поставок фронтенда и механизмы защиты.
При работе с фронтендом вы часто сталкиваетесь с ситуациями, когда необходимо использовать сторонние библиотеки, такие как jQuery или Bootstrap (первая имеет 4 миллиона загрузок в неделю на npm, а вторая — 3 миллиона). Оставим в стороне тот факт, что большинство людей сейчас используют webpack для упаковки своего кода. Ранее, для таких требований, вы либо загружали файлы библиотек самостоятельно, либо использовали готовый CDN для их загрузки.
Одним из источников является cdnjs, и его веб-сайт выглядит следующим образом:
Давайте рассмотрим практический пример!
Предположим, я работаю над веб-сайтом, который требует jQuery. Я бы использовал тег <script>
на странице для загрузки библиотеки jQuery, и источник мог бы быть следующим:
My own website
Предположим, я выбираю URL, предоставленный официальным сайтом jQuery. Я бы написал следующий HTML-код:
Таким образом, библиотека jQuery загружается, и другой код может использовать ее функциональность. Так почему бы мне не выбрать CDN вместо того, чтобы скачивать библиотеку и разместить ее на своем собственном сайте? Причин может быть несколько:
Лень, использование чужого решения быстрее.
Бюджетные соображения, использование чужого сайта экономит на расходах на пропускную способность и снижает нагрузку на мой собственный сайт.
Скорость.
Третий пункт, скорость, заслуживает особого упоминания. Если библиотека загружается с CDN, скорость загрузки может быть быстрее.
Причина более высокой скорости заключается в том, что CDN специально разработаны для этой цели и имеют узлы в разных странах. Например, если ваш сервер находится в Соединенных Штатах, и вы размещаете библиотеку на своем собственном сайте, пользователям из Тайваня придется подключаться к серверу в Соединенных Штатах, чтобы получить эти библиотеки. Однако, если вы используете URL, предоставленный CDN, им может понадобиться подключиться только к узлу в Тайване, что экономит время задержки.
В качестве примера возьмем известный сайт в Тайване, который использует ресурсы от Google и cdnjs:
Мы обсудили некоторые преимущества использования сторонних CDN. Теперь давайте рассмотрим недостатки.
Первый недостаток заключается в том, что если CDN выходит из строя, ваш сайт может также перестать работать. Даже если он не отключается полностью, он может испытывать замедление соединения. Например, если мой сайт загружает jQuery с cdnjs, но cdnjs внезапно начинает работать медленно, мой сайт также будет медленно загружаться, так как он на это влияет.
Второй недостаток заключается в том, что если CDN будет взломан и библиотека, которую вы импортировали, будет заражена вредоносным кодом, ваш сайт также будет скомпрометирован. Этот тип атаки известен как "supply chain attack"
(атака на цепочку поставок), когда компрометируется источник, что влияет на Downstream
.
Некоторые могут подумать: "Эти большие компании вряд ли будут взломаны, верно? И поскольку так много людей используют эти услуги, кто-то должен их мониторить."
Теперь давайте рассмотрим реальный случай.
Автор обнаружил уязвимость RCE в cdnjs, которая, если ее эксплуатировать, могла бы позволить контролировать весь сервис cdnjs.
Блог автора предоставляет подробный отчет о процессе. Здесь я кратко объясню, как формируется уязвимость, которая состоит из двух уязвимостей.
Во-первых, Cloudflare опубликовал код, связанный с cdnjs, на GitHub, и одна из его функций автоматического обновления привлекла внимание автора. Эта функция автоматически загружает упакованные файлы из npm в виде сжатых файлов (.tgz), распаковывает их и выполняет некоторую обработку перед копированием в соответствующее место.
Автор знал, что использование archive/tar для распаковки в Go может потенциально иметь уязвимости, потому что распакованные файлы не обрабатываются. Поэтому имена файлов могут выглядеть так: ../../../../../tmp/temp.
В чем проблема с этим?
Предположим, у вас есть кусок кода, который копирует файл и выполняет аналогичные операции, как показано ниже:
Конкатенируйте путь назначения и имя файла, чтобы создать целевое местоположение и создать новый файл.
Читать оригинальный файл и записывать его в новый файл.
Если путь назначения — /packages/test
, а имя файла — abc.js
, будет создан новый файл по адресу /packages/test/abc.js
.
Теперь, если путь назначения тот же, но имя файла — ../../../tmp/abc.js
, файл будет записан по адресу /package/test/../../../tmp/abc.js
, что соответствует /tmp/abc.js
.
Таким образом, используя эту технику, файлы могут быть записаны в любое место с соответствующими правами. Код cdnjs имеет аналогичную уязвимость, которая позволяет записывать файлы в любое место. Если эту уязвимость можно эксплуатировать для перезаписи файлов, которые запланированы для автоматического выполнения, можно достичь RCE.
Пока автор планировал создать POC для проверки этого, его заинтересовал способ работы функции автообновления Git (предыдущая дискуссия о сжатых файлах была связана с npm).
После исследования автор обнаружил кусок кода для копирования файлов, связанный с автообновлениями репозитория Git, который выглядит следующим образом:
На первый взгляд, это не выглядит как что-то особенное — просто копирование файла, открытие нового файла и копирование содержимого старого файла в него.
Но если оригинальный файл является символической ссылкой, ситуация меняется. Давайте кратко объясним, что такое символическая ссылка.
Концепция символической ссылки похожа на "ярлык", который мы видели в Windows. Этот ярлык сам по себе является просто ссылкой, указывающей на фактический объект.
В системах, подобных Unix, вы можете использовать команду ln -s target_file link_name
, чтобы создать символическую ссылку.
Чтобы лучше понять, приведем пример. Сначала я создаю файл с содержимым "hello" по адресу /tmp/hello. Затем в текущем каталоге создаю символическую ссылку, указывающую на только что созданный файл hello: ln -s /tmp/hello link_file
.
Если я выведу содержимое link_file
, оно покажет "hello"
, потому что на самом деле оно выводит содержимое /tmp/hello
. Если я запишу данные в link_file
, они фактически будут записаны в /tmp/hello
.
Теперь давайте попробуем написать кусок кода на Node.js для копирования файла и посмотрим, что произойдет:
После выполнения мы обнаружим, что в каталоге был создан файл test.txt
, и его содержимое совпадает с содержимым /tmp/hello
.
Таким образом, когда программа копирует файл, она не "копирует символическую ссылку", а фактически "копирует содержимое целевого файла".
Поэтому в ранее упомянутом коде на Go для копирования файлов, если есть файл, который является символической ссылкой, указывающей на /etc/passwd
, после копирования будет создан файл с содержимым /etc/passwd
.
Мы можем добавить символическую ссылку с именем test.js
в Git-файле, указывающую на /etc/passwd
. После копирования cdnjs
будет создан файл с именем test.js
, и его содержимое будет /etc/passwd
!
Это приводит к уязвимости Arbitrary File Read (произвольное чтение файла).
В заключение, автор обнаружил две уязвимости: одну для записи файлов и одну для чтения файлов. Запись файлов может привести к сбоям системы, если важные файлы случайно будут перезаписаны. Поэтому автор решил начать с уязвимости чтения файлов и создал репозиторий Git, опубликовал новую версию, дождался автообновления cdnjs и, наконец, активировал уязвимость чтения файлов. Содержимое прочитанного файла можно было увидеть в JS, опубликованном на cdnjs.
Файл, который автор прочитал, — это /proc/self/environ
(он изначально хотел прочитать другой файл — /proc/self/maps
), который содержит переменные окружения, включая ключ API GitHub. Этот ключ имеет права на запись в репозиторий под cdnjs, поэтому, используя этот ключ, можно напрямую изменить код cdnjs или сайт cdnjs, тем самым контролируя весь сервис.
Это объяснение уязвимости cdnjs. Если вы хотите увидеть больше технических деталей или подробное развитие событий, вы можете прочитать оригинальную статью автора, которая содержит много деталей. В любом случае, даже услуги, поддерживаемые крупными компаниями, подвержены риску компрометации.
Итак, как мы можем защититься от такого рода уязвимостей? Или, возможно, мы не можем защититься от них вовсе?
Браузеры на самом деле предоставляют функцию: "Не загружать файл, если он был изменен". Таким образом, даже если cdnjs будет скомпрометирован и файл jQuery будет изменен, мой сайт не загрузит новый файл jQuery, тем самым избегая атак с загрязнением файлов.
На cdnjs, когда вы решаете использовать определенную библиотеку, вы можете выбрать, чтобы скопировать URL или скопировать тег скрипта. Если вы выберете последний вариант, вы получите следующее:
Мы уже упоминали crossorigin="anonymous"
. Используя CORS
для отправки запросов, мы можем избежать отправки куки на бэкенд.
Другой атрибут, который выше, — это integrity
, и он является ключом к защите. Этот атрибут позволяет браузеру проверять, соответствует ли загружаемый ресурс предоставленному хеш-значению. Если оно не совпадает, это означает, что файл был изменен, и ресурс не будет загружен. Таким образом, даже если cdnjs будет скомпрометирован и хакер заменит файл react.js, который я изначально использовал, браузер не загрузит зараженный код, потому что хеш-значение не совпадает.
Однако этот метод может предотвратить только изменение "уже импортированных" скриптов. Если, по случайности, скрипт будет скопирован после того, как хакер изменил файл, то это не будет эффективно, потому что файл уже будет скомпрометирован.
Поэтому, если вы хотите полностью избежать этого риска, не используйте эти сторонние сервисы. Хостите эти библиотеки на своем собственном CDN, так что риск смещается с третьей стороны на вашу собственную службу. Если только ваша собственная служба не будет скомпрометирована, эти библиотеки должны быть в безопасности.
В настоящее время многие сайты используют сборщики, такие как webpack, для повторной упаковки библиотек, поэтому они не могут использовать сторонние библиотечные CDN. Они должны быть размещены на своих собственных сайтах, что устраняет этот тип атаки на цепочку поставок.
Тем не менее, имейте в виду, что вы все равно не сможете избежать других рисков атак на цепочку поставок. Даже если вы не используете сторонние библиотечные CDN, вам все равно нужно загружать эти библиотеки откуда-то еще, например, из npm. Если npm будет скомпрометирован и его файлы будут изменены, это все равно повлияет на вашу службу. Это атака на цепочку поставок, где атака не нацелена непосредственно на вас, а проникает через другие источники.
Тем не менее, этот тип риска можно смягчить во время сборки, используя службы статического сканирования для обнаружения измененных файлов или вредоносного кода. Некоторые компании также создают внутренние npm-реестры, которые не синхронизируются напрямую с внешними npm, что гарантирует, что используемые библиотеки не были изменены.
Существует множество методов атак, и исследователи, которые обнаружили уязвимость в cdnjs, в последнее время интересуются атаками на цепочку поставок.
Если вы хотите напрямую включить сторонние URL на своей странице, убедитесь, что вы проверили, что сайт надежен. Если возможно, также добавьте атрибут integrity
, чтобы предотвратить изменение файлов и последующее влияние на вашу службу. Также обратите внимание на конфигурацию CSP (Content Security Policy)
. Для сайтов, таких как cdnjs, если вы просто установите домен, уже существуют методы обхода, поэтому будьте осторожны при его настройке.
Я надеюсь повысить осведомленность среди фронтенд-разработчиков о атаках на цепочку поставок через уязвимость в cdnjs. Осознавая этот метод атаки, разработчики будут более осторожны в своем будущем развитии и обратят внимание на риски, связанные с импортом сторонних библиотек.
Помимо cdnjs, есть и другие сайты, которые предоставляют аналогичные услуги. Например, на официальном сайте , вы можете увидеть их собственный код.jquery.com, а Bootstrap использует сервис под названием .
jsDelivr:
cdnjs:
jQuery official:
Компания, стоящая за cdnjs, Cloudflare, действительно испытывала некоторые , которые затронули множество сайтов.
16 июля 2021 года исследователь безопасности опубликовал статью в своем блоге под названием .
Cloudflare также выпустила отчет о реагировании на инциденты через неделю:, в котором документируется последовательность событий и последующие меры по устранению. Они полностью переписали всю архитектуру, поместив часть, связанную с распаковкой, в Docker-песочницу, тем самым увеличив общую безопасность.
Если вы хотите узнать больше, вы можете обратиться к MDN, где есть страница, посвященная .