Не доверяй — проверяй: создатель curl о безопасности open source

Создатель curl Даниэль Стенберг описывает систему из 20+ мер верификации, которая защищает одну из самых распространённых библиотек в мире — от запрета бинарных блобов до обязательного фаззинга и torture-тестов. Разбираем, как применить эти практики к вашим зависимостям.

Обложка: Не доверяй — проверяй: создатель curl о безопасности open source

Вы уверены, что ваши зависимости — это именно то, что вы думаете? Даниэль Стенберг, создатель curl и один из самых авторитетных разработчиков в мире open source, рассказывает, почему слепое доверие к софту — путь к катастрофе, и как curl выстраивает многоуровневую систему верификации, которая позволяет спать спокойно при десятках миллиардов установок.

curl — одна из самых распространённых программных библиотек в мире. В форме libcurl она работает на десятках миллиардов устройств. При таком масштабе любая компрометация может иметь катастрофические последствия.

Ключевые выводы

— Каждый коммит и каждый релиз open-source проекта — потенциальная точка атаки: от внедрённого контрибьютора до взломанного CI

— curl применяет более 20 мер верификации: от запрета бинарных блобов и Unicode в коде до обязательного фаззинга и torture-тестов

— Внешние пользователи могут и должны верифицировать релизы: проверять подписи, сравнивать содержимое архивов с git-репозиторием

— Это не паранойя — это инженерная дисциплина, которая позволяет проекту оставаться надёжным уже 30 лет

— Стенберг призывает требовать такого же уровня верификации от всех зависимостей в вашем стеке

Атаки на supply chain: 9 сценариев угроз

Supply-chain атака — это компрометация не вашего кода, а кода ваших зависимостей: библиотек, инструментов сборки, CI-систем. С каждым коммитом и каждым релизом софта возникают риски. Даниэль Стенберг перечисляет сценарии, жертвой которых может стать широко используемый проект:

  • Внедрённый контрибьютор — как в случае с Jia Tan и xz-utils: внешне добросовестный участник команды, который намеренно маскирует вредоносный код под обычные коммиты
  • Скомпрометированный мейнтейнер — авторизованный разработчик, чья учётная запись или машина взломаны. Его коммиты и релизы теперь содержат заражённый код
  • «Полезный» баг-фикс от незнакомца — маленький шаг в длинной цепочке крошечных изменений, которые постепенно формируют уязвимость или бэкдор
  • Шантаж или давление — существующего участника проекта принуждают вносить изменения, которые иначе были бы отклонены
  • Непреднамеренная ошибка — добросовестный разработчик при добавлении фичи или исправлении бага случайно создаёт уязвимость
  • Подмена на уровне дистрибуции — сайт, с которого раздаются тарболлы (архивы с исходным кодом для релиза), взломан; вместо оригинальных архивов раздаётся малварь
  • Компрометация учётных данных — от имени известного участника проекта распространяется дезинформация через email, соцсети, блоги или даже дипфейк-видео
  • Атака через CI-инфраструктуру — инструмент, используемый в CI и размещённый в облаке, взломан и исполняет вредоносный код
  • Поддельное зеркало — пока основной git-репозиторий недоступен, кто-то предлагает «временное зеркало» с заражённым кодом

Любой из этих сценариев может произойти в комбинации с другими и в быстрой последовательности.

Верификация релизов: как пользователи могут проверить curl

Люди спрашивают Стенберга, как он спит по ночам, учитывая масштаб curl и количество возможных атак.

Есть только один способ бороться с этим видом бессонницы: делать всё возможное, и делать это открыто и прозрачно. Делать проект чуть лучше на этой неделе, чем на прошлой. Выстраивать инженерные процессы правильно. Предоставлять средства, чтобы каждый мог верифицировать то, что мы делаем и что мы выпускаем. Итерировать, итерировать, итерировать.
Даниэль Стенбергсоздатель и ведущий разработчик curl, wolfSSL

Если хотя бы несколько независимых пользователей проверят, что релиз curl подписан мейнтейнером, а содержимое совпадает с тем, что есть в git-репозитории, — проект в хорошем состоянии. Достаточно нескольких независимых наблюдателей, чтобы любая аномалия была замечена.

Стенберг подчёркивает: он не знает, кто эти пользователи и существуют ли они вообще. Они должны быть полностью независимы от него и от проекта curl. Но проект предоставляет все инструменты и делает верификацию максимально простой.

Безопасность curl изнутри: более 20 мер верификации

Внешние наблюдатели могут подтвердить, что релизы соответствуют тому, что есть в git. Но обеспечить, чтобы в git попадал только безопасный и корректный код, — это внутренняя работа команды. Вот полный список мер:

Стандарты кода и ограничения

  • Единый стиль кода — нарушение стиля вызывает ошибку сборки. Это снижает риск случайных ошибок и упрощает ревью
  • Запрет «опасных» C-функций — функции, которые легко использовать неправильно, запрещены. Их использование вызывает ошибку
  • Лимит на сложность функций — если функция слишком сложна, это ошибка сборки. Код должен быть читаемым и понятным
  • Запрет бинарных блобов в git — никаких способов спрятать зашифрованный вредоносный код. Попытка добавить блоб вызывает ошибку
  • Избегание base64-кодированных фрагментов — ещё один способ обфускации, который curl активно исключает
  • Ограничение Unicode в коде и документации — большинство использований Unicode запрещено, чтобы предотвратить homoglyph-атаки (подмену символов визуально похожими из другой кодировки, например кириллической «а» вместо латинской «a»)
  • Документирование всего — код, API, поведение — всё задокументировано, чтобы не было сюрпризов. Документация тестируется, проверяется спеллчекером и проходит ревью наравне с кодом

Тестирование и непрерывная интеграция

  • Обязательное ревью каждого PR — и людьми, и ботами. Коммиты ссылаются на исходный PR
  • Тысячи тестов — для каждой функции. Поиск «белых пятен» в покрытии — приоритетная задача
  • Более 200 CI-джобов — запускаются для каждого коммита и каждого PR. Без объяснённых провалов тестов мёрж невозможен
  • Максимально строгие опции компилятора — все предупреждения трактуются как ошибки через -Werror. Ни одно предупреждение не остаётся без внимания
  • Valgrind и санитайзеры — все тесты прогоняются через valgrind и несколько комбинаций санитайзеров для обнаружения проблем с памятью и неопределённого поведения (undefined behavior)
  • Torture-тесты — каждый тест перезапускается так, чтобы каждый вызов функции, способной завершиться ошибкой, упал ровно один раз. Это гарантирует: curl не утекает памятью и не падает при любых сбоях
  • Непрерывный фаззинг — автоматическое тестирование случайными входными данными для поиска крашей и уязвимостей. Работает без остановки в рамках Google OSS-Fuzz и кратко в CI для каждого коммита

Защита CI-инфраструктуры от компрометации

  • CI-джобы не пишут обратно в репозиторий — доступ только на чтение. Даже при компрометации CI код не может быть заражён
  • Анализ конфигов CI — инструменты вроде zizmor проверяют скрипты CI на уязвимости
  • Обязательная двухфакторная аутентификация — для всех коммиттеров на GitHub

Политика безопасности и стабильность API

  • Уязвимости исправляются в следующем релизе — без исключений. Проблемы безопасности не висят после того, как о них сообщили
  • Полная документация всех уязвимостей — каждая когда-либо найденная уязвимость curl задокументирована со всеми деталями
  • Стабильность ABI и API — curl никогда не ломает обратную совместимость (ABI — бинарный интерфейс приложения, API — программный интерфейс). Это позволяет пользователям легко обновляться до версий с исправленными уязвимостями вместо того, чтобы сидеть на устаревших
  • Независимые аудиты — код curl неоднократно проверяли внешние эксперты по безопасности. Все найденные проблемы были немедленно устранены

Всё это делается в открытую, с полной прозрачностью и отчётностью. Любой может наблюдать за процессом и проверять, что команда следует своим правилам.

Не паранойя, а инженерная дисциплина

Стенберг планирует на случай, когда кто-то действительно захочет и попытается навредить проекту и его пользователям. Или когда это произойдёт случайно. Успешная атака на curl теоретически может достичь миллиардов устройств.

Это не паранойя. Эта система позволяет нам спокойно спать по ночам. Именно поэтому пользователи до сих пор полагаются на curl спустя тридцать лет разработки.
Даниэль Стенбергсоздатель и ведущий разработчик curl, wolfSSL

Недавно Стенберг добавил на сайт curl отдельную страницу верификации, где подробно описано, как проверить целостность релизов.

Безопасность зависимостей: что проверить в вашем проекте

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

Проверка подписей и целостности пакетов

Большинство пакетных менеджеров умеют проверять подписи. Убедитесь, что эта проверка включена:

			# npm: проверка подписей реестра (доступно с npm 9+)
npm audit signatures

# pip: проверка хешей при установке
pip install --require-hashes -r requirements.txt

# GPG-проверка тарболла curl
curl -O https://curl.se/download/curl-8.12.1.tar.gz
curl -O https://curl.se/download/curl-8.12.1.tar.gz.asc
gpg --verify curl-8.12.1.tar.gz.asc curl-8.12.1.tar.gz
		

Аудит зависимостей в CI

Встройте проверку уязвимостей в ваш CI-пайплайн:

			# npm
npm audit --audit-level=high

# pip (через safety или pip-audit)
pip-audit

# Универсальный инструмент (поддерживает npm, pip, cargo, go и др.)
osv-scanner --lockfile=package-lock.json
		

Базы уязвимостей OSV и NVD — основные источники данных для этих инструментов.

Оценка практик ваших зависимостей

Прежде чем добавить зависимость, проверьте:

  • Есть ли у проекта обязательное ревью PR? Посмотрите историю мёржей
  • Есть ли CI с тестами? Проверьте бейджи в README и конфиги в .github/workflows/
  • Как быстро закрываются уязвимости? Проверьте security advisories на GitHub
  • Сколько активных мейнтейнеров? Один человек — точка отказа
  • Используется ли OpenSSF Scorecard — автоматическая оценка безопасности open-source проекта
Часто задаваемые вопросы
1
Что такое верификация релизов и как она работает на практике?

Верификация релизов — это проверка того, что скачанный софт действительно создан мейнтейнерами и не был изменён. На практике это включает два шага: проверку GPG-подписи (мейнтейнер подписывает архив своим ключом, вы проверяете подпись публичным ключом) и сравнение содержимого архива с тегом в git-репозитории. Для curl оба шага задокументированы на сайте проекта.

2
Чем случай с xz-utils отличается от обычного взлома?

В xz-utils атакующий (Jia Tan) не взломал аккаунт — он два года заслуживал доверие как активный контрибьютор, получил права мейнтейнера и лишь потом внедрил бэкдор. Это социальная инженерия уровня спецслужб, а не техническая атака. curl защищается от этого запретом бинарных блобов, обязательным ревью и запретом обфускации через base64 и Unicode — так вредоносный код сложнее спрятать даже доверенному участнику.

3
Что такое torture-тесты и можно ли применить их в своём проекте?

Torture-тесты в curl — это перезапуск каждого теста с принудительным падением каждой функции, способной вернуть ошибку. Если malloc вернул NULL, если fopen не открыл файл — curl не должен упасть или утечь памятью. Для своего проекта аналогичный подход можно реализовать через fault injection: библиотеки вроде libfiu (C) или test-butler (Android) позволяют имитировать сбои системных вызовов.

4
Как начать фаззить свой open-source проект?

Если ваш проект на C/C++, подайте заявку в Google OSS-Fuzz — сервис бесплатно и непрерывно фаззит open-source проекты. Для локального фаззинга используйте libFuzzer (интегрирован в Clang) или AFL++. Для Go есть встроенный фаззинг с Go 1.18, для Rust — cargo-fuzz. Начните с парсеров входных данных — это самые частые точки уязвимостей.

5
Почему curl запрещает большинство использований Unicode в коде?

Это защита от homoglyph-атак: визуально похожие Unicode-символы подменяют ASCII. Кириллическая «а» идентична латинской «a» на экране, но это разные байты. Атакующий может назвать переменную с подменённым символом, и ревьюер не заметит разницу. Аналогичную защиту реализует компилятор Rust (предупреждение mixed_script_confusables) и инструмент trojan-source для других языков.

Выводы: доверяйте коду, а не репутации

Опыт curl показывает: безопасность open source — это не вопрос доверия, а вопрос верификации. За 30 лет разработки проект выстроил систему из более чем 20 конкретных мер — от запрета бинарных блобов до непрерывного фаззинга — которые вместе создают многоуровневую защиту.

Если проект с десятками миллиардов установок может работать полностью открыто и предоставлять инструменты верификации для каждого, то и другие проекты обязаны стремиться к тому же стандарту. Начните с малого: npm audit signatures, pip-audit, проверка security advisories ваших зависимостей.

Не доверяйте — проверяйте.

Источник: Don't trust, verify — блог Даниэля Стенберга