Руководство для мейнтейнеров PHP-проектов от команды безопасности экосистемы PHP Foundation

Вы поддерживаете PHP-проект. Кто-то (возможно, Фолькер из PHP Foundation или независимый исследователь) только что сообщил вам, что в вашем проекте может быть уязвимость. Вы можете чувствовать растерянность, не быть уверенным в том, стоит ли доверять этому отчету, или просто не понимать, каким должен быть правильный следующий шаг.

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

И если на каком-то этапе вы зайдете в тупик:  вы не одни. Вы всегда можете обратиться за помощью к команде безопасности экосистемы. Контактные данные указаны в конце этого руководства.


Как пользоваться этим руководством

  •  Не читайте его подряд от начала до конца. Перейдите к тому этапу, на котором вы находитесь сейчас. Каждый раздел начинается с краткого указателя «Вы находитесь здесь, если…».
  •  Рассчитывайте вернуться к нему через несколько дней. Правильная обработка отчета об уязвимости обычно занимает больше одного дня, и это нормально.
  •  Если у вас есть всего пять минут прямо сейчас, прочтите шпаргалку и раздел 1.

Пара слов о ваших полномочиях перед началом:  вы — мейнтейнер. Вы решаете, что входит в область видимости (scope), каковы сроки и является ли отчет действительным. Успешная обработка отчета не означает, что нужно бросить все дела или работать по выходным. Это значит — выполнить небольшое количество шагов в правильном порядке, в комфортном для вас темпе. Вы никому не обязаны совершать подвиги; см. статью Мейнтейнеры открытого ПО вам ничего не должны.


Шпаргалка

Весь процесс на одном экране. Подробности — в пронумерованных разделах ниже.

  1.  Не паникуйте и не пре предавайте огласке. Сохраняйте конфиденциальность уязвимости до выпуска исправления. → §1
  2.  Подтвердите получение отчета в течение нескольких дней, даже до того, как оцените его. → §2
  3.  Триаж: разберитесь в сути утверждения, затем примите решение: действительно ли это проблема, ложная тревога или она вне зоны ответственности. → §3
  4.  Безопасно воспроизведите проблему. Никогда не запускайте код доказательства концепции (PoC) на собственной машине; используйте изолированное окружение. → §4
  5.  Исправляйте конфиденциально. Используйте временный приватный форк; не пушьте код в публичные ветки. → §5
  6.  Напишите бюллетень безопасности (advisory). Для Composer-пакетов правильную работу инструментов обеспечивают три поля: укажите экосистему  Composer, точное имя пакета в Packagist и четкие диапазоны затронутых версий. Composer будет активно  блокировать установку указанных вами версий. → §6
  7.  Публикуйте в правильном порядке: мёрдж → тег и релиз → публикация advisory → отправка в FriendsOfPHP → анонс. → §7
  8.  Завершение: поблагодарите автора отчета, проверьте старые ветки, проведите краткую ретроспективу. → §8
  9.  Улучшите свою защиту, чтобы следующий отчет дался проще: SECURITY.md, приватный репорты уязвимостей, 2FA, усиленная CI. → §9
  10.  Просите о помощи в любых сомнительных ситуациях. → §10

1. Не паникуйте и не предавайте огласке

Вы находитесь здесь, если: только что получили отчет, и у вас участился пульс.

Самый важный принцип —  координированное раскрытие информации (coordinated disclosure): детали уязвимости остаются конфиденциальными до тех пор, пока не появится исправленная версия и пользователи не смогут себя защитить. В тот момент, когда детали становятся публичными, они оказываются у каждого злоумышленника в мире, а у ваших пользователей еще нет исправления. Поэтому до момента публикации:

  •  Не открывайте публичные issue, не ссылайтесь на проблему в публичном пулл-реквесте и не упоминайте ее в сообщениях публичных коммитов.
  •  Не отправляйте исправление (push) в ветку  main  или любую другую публичную ветку. Коммит с названием «исправление SQL-инъекции в обработчике входа» и есть раскрытие информации.
  •  Не публикуйте детали в социальных сетях, блоге или рассылке. Избегайте расплывчатых намеков, которые только подстегивают людей искать уязвимость. Анонс предстоящего релиза безопасности — это нормальная практика, которой следуют многие проекты, чтобы пользователи могли заранее запланировать обновление. Если вы делаете такой анонс, ограничьтесь датой и фактом того, что готовится релиз безопасности; никогда не упоминайте компонент, симптомы или затронутые версии.
  •  Не игнорируйте автора отчета. Молчание — это то, из-за чего доброжелательные исследователи превращаются в разочарованных людей, которые в итоге публикуют всё самостоятельно.

Также важно с самого начала усвоить:  отчет — это утверждение, а не приговор. Это может быть серьезная находка, незначительная проблема, дубликат, баг без влияния на безопасность или просто ошибка. Часть вашей работы на следующих этапах — выяснить, что именно перед вами, и вы принимаете это решение, желательно в диалоге с автором отчета.

Если отчет поступил от команды безопасности экосистемы PHP Foundation, он уже прошел первичный триаж и обычно сопровождается воспроизводящим скриптом. Это повышает вероятность того, что проблема реальна, но окончательное решение все равно остается за вами.

2. Подтвердите получение отчета

Вы находитесь здесь, если: вы один раз прочитали отчет и еще не ответили на него.

Ответьте быстро, даже если вам пока нечего сказать по существу. Короткое сообщение сэкономит вам время и сохранит расположение автора:

Спасибо за отчет. Я получил его и изучу при первой возможности. Я поддерживаю этот проект в свободное время. Я вернусь к вам здесь же.

Это действительно всё, что требуется. Если вы уже знаете, что сможете выполнить взятые на себя обязательства, указание временных рамки («пожалуйста, дайте до двух недель на первичную оценку») будет проявлением вежливости по отношению к исследователю, но обещайте только то, что реально можете выполнить: лучше сдержать обещание в «две недели», чем нарушить обещание сделать «завтра». Если отчет пришел по электронной почте, но в вашем репозитории включена функция приватного репорта уязвимостей на GitHub (см. §9), это отличный момент перенести общение туда: это позволит хранить обсуждение и патч в одном конфиденциальном месте. Однако не рассчитывайте, что это поможет вам спланировать релиз — в бюллетене нет поля для даты эмбарго или раскрытия, поэтому сроки вы с исследователем согласовываете в ходе беседы, а затем их придерживаетесь.

3. Триаж отчета

Вы находитесь здесь, если: вы подтвердили получение отчета и теперь должны решить, что с ним делать.

Внимательно прочитайте отчет и постарайтесь ответить на четыре вопроса:

  1.  В чем суть заявленной уязвимости? (например, SQL-инъекция, обход пути (path traversal), небезопасная десериализация; часто указывается в виде идентификатора CWE)
  2.  Кто и откуда может ее вызвать? Требуется ли аутентифицированный администратор, или ее может вызвать любой анонимный пользователь в интернете? Требуется ли какая-то необычная конфигурация?
  3.  Чего на самом деле может добиться злоумышленник? Чтение данных, модификация данных, выполнение кода, отказ в обслуживании (DoS)?
  4.  Какие версии затронуты? Включая старые мажорные и минорные версии, которые люди все еще используют.

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

  •  Задавайте вопросы. Хороший исследователь предпочтет ответить на три уточняющих вопроса, чем наблюдать, как вы гадаете на кофейной гуще. Если модель угроз неясна («можно ли это проэксплуатировать, если злоумышленник контролирует это значение конфигурации? В моем проекте они никогда этого не делают»), скажите об этом. Такое обсуждение — самая ценная часть триажа.
  •  «Работает по задумке» (Works as designed) — вполне правомерный ответ. Если для эксплуатации проблемы злоумышленник уже должен обладать правами, которые согласно вашей документированной модели безопасности считаются доверенными, это может не являться уязвимостью в вашем проекте. Объясните свои соображения автору отчета; если он не согласен, команда безопасности экосистемы может выступить в роли нейтрального эксперта.
  •  Реальный баг, не влияющий на безопасность, все равно стоит исправить, но через ваш обычный публичный процесс, без выпуска бюллетеня безопасности.

Если объем или качество отчетов сами по себе становятся проблемой (например, поток низкокачественных отчетов, сгенерированных ИИ), это как раз одна из тех задач, для решения которых существует команда безопасности экосистемы. Пересылайте их им; не позволяйте им тратить вашу мотивацию.

4. Безопасное обращение с кодом PoC

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

Этот раздел существует потому, что описывает ошибку с худшим сценарием последствий.

 Никогда не запускайте код доказательства концепции (PoC) напрямую на собственном компьютере. Не «только на этот раз», не потому, что исследователь кажется заслуживающим доверия, и не потому, что вы бегло просмотрели код и он выглядел безобидно. На вашей рабочей машине хранятся ваши SSH-ключи, GPG-ключи, учетные данные Packagist и GitHub, менеджер паролей и, если вы им пользуетесь, ваш ИИ-ассистент с доступом к аккаунтам. Вредоносный или просто небрежный PoC, запущенный от имени вашего пользователя, может скомпрометировать их все. Для мейнтейнера это не просто личная проблема:  ваши учетные данные — это вектор supply-chain атаки на каждого, кто устанавливает ваш пакет.

И помните, чем является PoC: это программа, написанная незнакомцем и предназначенная для взлома ПО. Относитесь к нему ровно с тем уровнем подозрительности, которого заслуживает это описание. Даже PoC от честного исследователя может удалить файлы, открыть сетевые соединения или положить процессор в качестве побочного эффекта демонстрации бага.

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

  •  Песочница microVM — идеальный баланс: примерно так же удобно, как контейнер, но запускает собственное ядро вместо того, чтобы разделять хостовое, поэтому изоляция обеспечивается на уровне железа. Docker Sandboxes ( sbx ) предоставляет каждой песочнице собственную файловую систему, стеки сети и демон Docker, причем сетевой доступ по умолчанию запрещен. Бенджамин Эберлей (Benjamin Eberlei) описывает готовую настройку sbx с PHP, Composer и обычными расширениями, которую можно сразу перенести в ваш рабочий процесс.
  •  Контейнер (Docker/Podman) удобен для типичных проблем на уровне PHP и отлично подходит для большинства уязвимостей веб-приложений. Степень изоляции зависит от того, где вы его запускаете: в Linux контейнеры разделяют ядро хоста, поэтому все, что пахнет повреждением памяти (memory corruption), нативными расширениями или взаимодействием с ядром, требует более сильной изоляции. На macOS и Windows Docker Desktop и OrbStack уже запускают ваши контейнеры внутри легкой Linux-виртуалки, поэтому граница ядра находится между контейнером и вашей реальной машиной.
  •  Хорошо защищенная виртуальная машина — вариант «и ремень, и подтяжки»: свежая ВМ с актуальной ОС, без учетных данных или личных данных внутри, без общих папок с хостом, с отключенным общим буфером обмена и снапшотом, сделанным до того, как вы что-либо запустите, чтобы потом можно было откатиться. Если PoC не нужен доступ в интернет, отключите сеть полностью или ограничьте ее режимом «только хост» (host-only).
  •  Облачная одноразовая машина (недолговечная ВМ у любого провайдера, уничтожаемая после использования) тоже подойдет, если на ней не хранятся ваши учетные данные.

Внутри изолированного окружения рабочий процесс прост: извлеките затронутую версию вашего проекта, установите зависимости, запустите PoC, наблюдайте. Прочитайте PoC перед запуском. Не в качестве замены изоляции, а потому, что понимание того, как именно он вызывает баг — это ровно то понимание, которое нужно для исправления и регрессионного теста. По сути, это естественный момент для превращения воспроизводящего скрипта в падающий тест (§5). Многие мейнтейнеры считают момент «у меня есть тест, который падает» моментом принятия отчета, на том основании, что нельзя принять то, что нельзя воспроизвести.

Два заключительных правила для этого раздела:

  •  Специально сформированные файлы входных данных — это код. «Безобидный»  .phar , картинка, XML-документ или сериализованный пейлоад, прикрепленные к отчету — это по определению средство доставки эксплойта. К их открытию или парсингу применяются те же правила изоляции.
  •  Если вы не можете воспроизвести проблему безопасно, попросите о помощи, вместо того чтобы идти по сокращенному пути. Создание воспроизводящих скриптов в изолированных средах — одна из ключевых услуг команды безопасности экосистемы.

5. Подготовка исправления в конфиденциальном режиме

Вы находитесь здесь, если: проблема подтверждена, и вы готовы писать код.

Бюллетени безопасности GitHub предоставляют вам  временный приватный форк ровно для этой цели. На странице бюллетеня нажмите «Start a temporary private fork». Затем:

  1. Всю работу выполняйте во временном приватном форке и открывайте пулл-реквест  там же, ни в коем случае не в публичном репозитории.
  2.  Сначала напишите регрессионный тест. Тест, который падает на уязвимом коде — это доказательство того, что вы действительно поняли и воспроизвели проблему, а после применения исправления он больше не будет падать. Если вы создали воспроизводящий скрипт во время триажа (§4), у вас уже есть большая его часть; превратите его в безопасный, минимальный тест, демонстрирующий условие, а не поставляющий готовый эксплойт. Это та часть работы, которая продолжает окупаться в дальнейшем: баг уже никогда не вернется незаметно.
  3.  Затем напишите исправление и сохраняйте нейтральный тон сообщений коммитов в процессе работы; всю историю бюллетень расскажет позже. (Ссылки на GHSA ID в финальном коммите допустимы, так как он станет публичным только после публикации).
  4.  Проверьте проект на наличие аналогичных багов, прежде чем двигаться дальше. Публикация бюллетеня привлекает внимание к этому классу уязвимостей — как со стороны людей-читателей, так и со стороны тех, кто натравив LLM на ваш код, хочет быть уверенным, что точно такая же ошибка не прячется в соседних файлах, ожидая своего часа на следующий день после релиза.
  5. Пригласите автора отчета в качестве соавтора (collaborator) к бюллетеню и позвольте ему проверить исправление. У него уже есть работающий PoC, так что это не потребует больших усилий. Какой вес имеет его подтверждение — решать вам: исследователь, предоставивший точный воспроизводящий скрипт, лучше всего подходит для подтверждения закрытия бреши, в то время как отчет, сгенерированный инструментом массово, — нет.
  6. Решите,  какие версии получат исправление. Если вы все еще поддерживаете старые мажорные версии, пользователи этих версий также заслуживают патча или явного указания в бюллетене на необходимость обновления. Серьезность, возраст старой версии и трудозатраты на бэкпорт должны учитываться при принятии этого решения.

Подводные камни, на которых спотыкаются новички:

  • CI не запускается во временных приватных форках. Запускайте набор тестов локально (в безопасном окружении) перед слиянием (merge).
  • Не делайте  git push --no-verify  по привычке. Хуки, сканирующие код на наличие секретов или выполняющие проверки, существуют ровно для таких дней.
  • Временный приватный форк  не переживет сам бюллетень: в зависимости от того, на каком этапе вы находитесь, публикация удалит его, либо GitHub попросит вас удалить его перед публикацией. В любом случае убедитесь, что все необходимое (коммиты, результаты обсуждения) предварительно попало в какое-то постоянное хранилище.
  •  Не исправляйте молча. Патч, выпущенный без бюллетеня безопасности, оставляет каждого пользователя вслепой зоне:  composer audit  не предупредит их, Dependabot не откроет пулл-реквест, а большинство людей обновляют зависимости нечасто, поэтому они остаются уязвимыми до тех пор, пока это не произойдет случайно. Бюллетень — это не признание поражения; это механизм, с помощью которого ваше исправление действительно доходит до людей.

6. Написание бюллетеня безопасности GitHub

Вы находитесь здесь, если исправление готово, и вы заполняете форму бюллетеня.

Вы создаете бюллетень в своем репозитории в разделе  Security → Advisories → New draft security advisory. Пошаговый разбор этой формы (каждое поле, калькулятор CVSS, добавление авторов, добавление нескольких затронутых продуктов) описан в документации GitHub Создание бюллетеня безопасности репозитория. (Если отчет поступил через функцию приватных репортов, проект бюллетеня уже существует; редактируйте его, а не создавайте новый). Этот раздел посвящен тому, что инструкция не может сделать за вас: что именно писать внутри полей.

Бюллетень безопасности — это документ, предназначенный как для чтения людьми, так и для автоматической обработки машинами. Инструменты по всей экосистеме, включая Базу данных бюллетеней GitHub, OSV.dev, Dependabot, Packagist и  composer audit , используют его для автоматического предупреждения ваших пользователей. Правильное заполнение метаданных заставляет этот механизм работать.

 Для PHP-пакетов, устанавливаемых через Composer, ключевые детали таковы:

  •  Экосистема (Ecosystem): выберите  Composer . Не «Other», не «GitHub Actions», не оставляйте пустым. Только бюллетени, поданные в экосистеме Composer, сопоставляются с файлами  composer.json  /  composer.lock , и именно это заставляет срабатывать  composer audit  и уведомления Dependabot для ваших пользователей.
  •  Имя пакета (Package name): точное имя в Packagist в формате  vendor/package . Например,  phpunit/phpunit , а не  PHPUnit  и не слаг репозитория GitHub, если они отличаются. Опечатка здесь молчаливо ломает сопоставление. Проверка занимает пять секунд: добавьте то, что вы ввели, к адресу  https://packagist.org/packages/  — вы должны попасть на страницу своего пакета, как это происходит с packagist.org/packages/phpunit/phpunit.
  •  Диапазоны затронутых версий (Affected version ranges): точные и в синтаксисе Composer, например  >= 10.0.0, < 10.5.17 , по одному элементу Affected product на каждую затронутую линейку релизов, если вы пропатчили несколько (одно поле не может содержать несколько диапазонов). Точный синтаксис имеет неочевидные правила (поддерживаемые операторы, пробелы, порядок сортировки суффиксов вроде  -beta1 ), которые описаны в руководстве GitHub Лучшие практики написания бюллетеней безопасности репозиториев, с которым стоит ознакомиться за пять минут до заполнения этого поля. Именно по нему работает резолвер версий; текст «все версии до X» в описании не заменяет его. И правильность этих диапазонов важна как никогда раньше, потому что Composer больше не просто сообщает о вашем бюллетене. Он его принудительно исполняет. Читайте дальше.
  •  Исправленная версия (Patched version): версия, которую вы собираетесь выпустить, та, на которую вы хотите перевести пользователей. Это то, что инструмент предлагает людям в качестве выхода, поэтому она должна быть указана в форме, даже если вы еще не пометили ее тегом во время создания черновика бюллетеня.

Composer блокирует версии, указанные в вашем бюллетене

Начиная с Composer 2.9, принудительное применение бюллетеней безопасности встроено непосредственно в резолвер зависимостей и включено по умолчанию для каждого PHP-проекта. Когда Composer разрешает зависимости ( composer update ,  require ,  remove ), версии, подпадающие под опубликованный бюллетень, удаляются из пула кандидатов до запуска алгоритма разрешения. С точки зрения резолвера они просто не существуют. Composer 2.10 обобщил это в единый фреймворк политик зависимостей (dependency policy), который также охватывает пакеты, помеченные как вредоносное ПО (они блокируются даже во время  composer install  из существующего lock-файла), и заброшенные пакеты. Этот механизм пришел на смену старому подходу с конфликтующими пакетами  roave/security-advisories . Подробную механику (фильтрация пула, конфигурация  config.policy , правила игнорирования, обходные пути) см. в статьях Вышибала в резолвере зависимостей и обновлении безопасности цепочки поставок Packagist.

Для вас как автора бюллетеня это имеет три практических последствия:

  1.  Ваш бюллетень напрямую защищает пользователей, даже тех, кто его никогда не читал. Это сильнейший аргумент против молчаливого исправления (§5): бюллетень не просто информирует, он активно удерживает уязвимые версии вне директорий  vendor/  по всей экосистеме.
  2.  Слишком широкие диапазоны версий вызывают реальные сбои. Как только данные вашего бюллетеня дойдут до Composer (см. шаг 4 в §7 о том, сколько времени это занимает), каждая покрытая ими версия станет недоступной для установки по умолчанию — везде и для всех. Если в диапазоны попадут версии, которые никогда не были уязвимы, пользователи этих версий столкнутся с падающими сборками, и отчеты об ошибках полетят в ваш инбокс. Это не гипотетично: из-за бюллетеня PHPUnit в апреле 2026 года GitHub автоматически переписал аккуратно указанные затронутые версии ( 12.5.21  и  13.1.5 ) в широкие диапазоны, из-за чего любая старая версия PHPUnit стала неустанавливаемой за одну ночь, включая весь PHPUnit 11, который вообще не был затронут. Полная хронология задокументирована в статье Вышибала в резолвере зависимостей.
  3.  Знайте о маршруте FriendsOfPHP в обоих направлениях. Packagist агрегирует бюллетени из базы данных GitHub Advisory и из репозитория  FriendsOfPHP/security-advisories , причем данные FriendsOfPHP имеют приоритет. Это делает пулл-реквест туда одновременно самым быстрым способом донести ваш бюллетень до Composer в первую очередь (§7) и самым быстрым способом исправить то, что блокирует Composer, если опубликованные данные о затронутых версиях окажутся неверными — будь то по вашей собственной ошибке или из-за правок на стороне GitHub. Для исправления отправьте пулл-реквест и в Базу данных бюллетеней GitHub тоже, чтобы оба источника не противоречили друг другу.

Об обратной стороне медали стоит рассказать и вашим пользователям (в анонсе или документации): с актуальным Composer они защищены по умолчанию, а команда  composer audit  в CI подскажет им, когда в уже зафиксированной (locked) версии появилась уязвимость.

Остальная часть формы:

  •  Заголовок (Title): одна строка, конкретная, без драматизма. «SQL-инъекция при экспорте отчетов через параметр  sort » звучит лучше, чем «Критическая проблема безопасности».
  •  Описание (Description): достаточно, чтобы пользователь понял, затронут ли он и что делать (обновиться до какой версии; обходной путь, если он существует). Стремитесь к золотой середине: слишком много деталей дает злоумышленникам готовый эксплойт, слишком мало — морит голодом инструменты и читающих людей. Вам не нужно включать PoC.
  •  CWE: выберите ближайший класс уязвимостей. Предложение автора отчета обычно верно.
  •  CVSS / степень угрозы (Severity): оценивайте честно. Если требуемые условия снижают практическую опасность (требуется аутентификация, нестандартная конфигурация), отразите это в векторе, а не в споре в описании. Тем не менее, сохраняйте пропорции: оценка гораздо менее важна, чем сам бюллетень. Если от цифр CVSS у вас замыливается глаз, LLM сгенерирует для вас разумный первый вектор, а команда безопасности экосистемы с радостью его проверит; помощь с оценкой — это минутная услуга. Не позволяйте цифре задерживать публикацию.
  •  CVE: вы можете запросить CVE ID через GitHub прямо из формы бюллетеня (GitHub является CNA, и это нормальный путь для PHP-пакетов). Четко понимайте, что он вам дает. CVE ID — это стабильный идентификатор для людей за пределами вашей непосредственной экосистемы: дистрибутивов Linux, поставляющих ваш код, корпоративного управления уязвимостями, других CNA, координирующих общую проблему. То, что реально защищает среднего пользователя вашего пакета — это GHSA, с которым работают Composer,  composer audit  и Dependabot. Запросы CVE через GitHub в настоящее время занимают недели, поэтому относитесь к идентификатору как к тому, что приходит само по себе: запрашивайте его, как только приняли отчет, и никогда не задерживайте исправление, релиз или бюллетень в ожидании его получения. Если координирующая сторона, например CNA, уже зарезервировала CVE для этой проблемы, выберите пункт «У меня есть существующий CVE ID» вместо запроса второго.
  •  Авторы (Credit): добавьте автора отчета (и всех, кто помогал) в раздел благодарностей. Это ничего вам не стоит и является большой частью того, почему ответственное информирование стоит усилий исследователя.

7. Координация релиза и публикации

Вы находитесь здесь, если: исправление готово, бюллетень составлен, все участники приватного обсуждения согласны.

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

  1.  Слейте (Merge) исправление из приватного форка.
  2.  Пометьте тегом и выпустите (Tag and release) исправленную версию (версии) и убедитесь, что релиз действительно появился на Packagist, прежде чем продолжать.
  3.  Опубликуйте бюллетень. С этого момента уязвимость становится публичной, и это нормально, так как исправление уже опубликовано тоже.
  4.  Отправьте пулл-реквест в  FriendsOfPHP/security-advisories . Не пропускайте этот шаг под предлогом того, что вы уже создали GHSA. Composer не смотрит на бюллетени, опубликованные в отдельных репозиториях; он работает через Packagist, который использует проверенную базу данных GitHub Advisory, а этап проверки GitHub в настоящее время отстает от публикации на уровне репозитория на дни или даже недели. Пока ваш бюллетень не пройдет эту очередь,  composer audit  будет молчать, а резолвер зависимостей продолжит раздавать уязвимые версии. Пулл-реквест в FriendsOfPHP — это то, что оперативно закрывает этот разрыв. И если ваш проект вообще не размещен на GitHub, это не оптимизация: это единственный путь в инструменты экосистемы.
  5.  Анонсируйте выпуск через ваши обычные каналы (заметки о рельсах, Mastodon, Discord, блог со ссылкой на бюллетень). Излагайте факты: в чем проблема, кто затронут, до какой версии обновляться.

Разрыв между шагом 2 и шагом 3 должен составлять от минут до часов, а не дней: публичное исправление без бюллетеня — это окно, в течение которого злоумышленники могут сдиффить ваш релиз, в то время как у ваших пользователей нет предупреждения об обновлении.

Два замечания по координации:

  • Если отчет поступил через координатора вроде CNA, заранее согласуйте с ним дату публикации, чтобы публикация CVE и любые более широкие анонсы совпали по времени. Команда безопасности экосистемы обычно не просит координировать с нами ваш релиз; если конкретный случай когда-либо этого потребует, мы скажем об этом прямо. Мы не хотим вставать между отчетом и исправлением.
  • Если детали утекут раньше времени (кто-то написал в Твиттере, появилось публичное issue, вы заметили обсуждение бага), эмбарго фактически окончено. Опубликуйте то, что у вас есть, как можно скорее, даже если ситуация не такая аккуратная, как вам хотелось бы.

8. Подведение итогов

Вы находитесь здесь, если бюллетень опубликован и адреналин отступает.

  •  Убедитесь, что конвейер сработал: бюллетень появился в базе данных GitHub Advisory,  composer audit  помечает уязвимую версию в тестовом проекте, а оповещения Dependabot срабатывают там, где ожидалось. Не ждите всего этого в день публикации: бюллетень в вашем репозитории должен пройти очередь проверки GitHub, прежде чем его увидит Composer, и именно для обхода этого ожидания предназначен пулл-реквест в FriendsOfPHP (§7). Если он все еще не появился через несколько дней, проверьте поля экосистемы и имени пакета (§6); это главные подозреваемые, когда сопоставление молчаливо падает.
  •  Убедитесь, что блокировка не слишком агрессивна: в черновом проекте проверьте, что исправленная версия и незатронутые линейки релизов по-прежнему устанавливаются и обновляются без проблем. Сравните диапазоны затронутых версий, показанные в Базе данных бюллетеней GitHub, с тем, что написали вы (известны случаи, когда они менялись во время публикации), и следите за трекером проблем на предмет жалоб «не удается установить версию X» в первые дни после публикации. Способ исправления описан в §6.
  •  Поблагодарите автора отчета еще раз, публично, если он не против.
  •  Проверьте другие поддерживаемые ветки в последний раз; легко пропатчить текущую мажорную версию и забыть про LTS-ветку.
  •  Проведите с собой пятнадцатиминутную ретроспективу: Как этот баг попал внутрь? Поймал бы его статический анализатор, более строгий тип или соглашение о тестах? Проверка на наличие родственников этого бага уже должна быть позади к этому моменту (§5); здесь вы ищете то одно структурное изменение, которое предотвратит повторение всего класса. Одно такое улучшение на инцидент быстро дает кумулятивный эффект.

9. Улучшение безопасности в долгосрочной перспективе

Вы находитесь здесь, если буря улеглась, и вы хотите, чтобы следующая была меньше. Этот раздел также является отличной отправной точкой, если отчет еще не поступал, и вы просто хотите подготовиться.

Вам не нужно делать все это за одни выходные. Список примерно упорядочен по соотношению пользы и затрат; даже первые три пункта ставят вас впереди большинства проектов.

 Упростите отправку отчетов вам

  1.  Включите приватные репорты уязвимостей на GitHub (repository → Settings → Advanced Security → Private vulnerability reporting → Enable). Без этого исследователи вынуждены выбирать между холодным письмом на электронную почту и открытием публичного issue, и некоторые выберут публичное issue.
  2.  Добавьте файл  SECURITY.md , в котором укажите, как сообщать об уязвимостях (в идеале: «используйте приватные репорты уязвимостей в этом репозитории»), какие версии вы поддерживаете с исправлениями безопасности и какого времени ответа примерно ожидать. Три честных предложения лучше страницы канцелярского текста.
  3.  Следите за своей вкладкой безопасности (security tab). Туда попадают две разные вещи, и обе заслуживают уведомления: бюллетени безопасности (security advisories), куда приходят отчеты о  вашем коде, и оповещения Dependabot (Dependabot alerts), которые сообщают, что используемая  вами зависимость имеет опубликованную уязвимость. Убедитесь, что получаете уведомления о том и о другом: отслеживайте репозиторий с включенными предупреждениями безопасности и проверьте настройки уведомлений в своей учетной записи GitHub, чтобы ни то, ни другое не оставалось без внимания месяцами.

 Защитите свои аккаунты и релизы

  1.  Надежная 2FA/MFA везде, где можно публиковать: GitHub, Packagist, ваш почтовый ящик (с помощью которого можно сбросить два предыдущих). Предпочитайте пасскеи или аппаратные ключи SMS-сообщениям. Проверьте, у кого еще есть права на публикацию, и удалите устаревший доступ. На Packagist это становится видимым свойством ваших пакетов, а не просто внутренней гигиеной: Packagist.org будет публично выводить статус MFA мейнтейнеров в журнале прозрачности и в профилях, причем обязательная MFA заявлена как долгосрочное направление. Большинство недавних атак на цепочку поставок в экосистеме PHP начались с захвата аккаунта мейнтейнера.
  2.  Защитите свой путь релиза: защита веток для  main , защита тегов для релизных тегов и никаких долгоживущих персональных токенов доступа с широкими полномочиями, валяющихся в секретах CI или на диске.

 Уменьшите поверхность атаки

  1.  Удалите то, чего не должно существовать. Каждая ветка в вашем репозитории — это дверь: цель для пулл-реквестов, приносящих с собой измененные файлы воркфлоу, скрипты сборки или конфигурацию, которую конвейер с радостью выполнит. Этот класс атак известен как Poisoned Pipeline Execution (PPE / отравление выполнения конвейера) (OWASP CICD-SEC-4). Ветка, которой не существует, не может получить отравленный пулл-реквест, и ни одна дверь не защищает лучше той, которая никогда не была построена. Пересмотрите свои ветки и удалите устаревшие эксперименты, остатки старых спринтов и рудименты из серии «возможно, нам это еще пригодится». То же самое относится к неиспользуемым воркфлоу, сторонним экшенам и разрешениям, которые больше никто не ставит под сомнение. Полный аргумент см. в статье Поверхность атаки начинается в репозитории, включая реальные инциденты PPE от SolarWinds до PyTorch.

 Усильте автоматизацию

  1.  Относитесь к файлам воркфлоу как к коду, имеющему отношение к безопасности. Они работают с учетными данными, сетевым доступом и правом записи в ваш репозиторий. Пять классов уязвимостей, которые обнаруживаются почти везде, включая собственные воркфлоу PHPUnit (52 находки до усиления безопасности, ноль после):

    •  Инъекции шаблонов (Template injection): никогда не интерполируйте выражения  ${{ ... }}  (имена веток, заголовки пулл-реквестов и другие контролируемые злоумышленником значения) напрямую в скрипты оболочки (shell scripts); вместо этого передавайте их через переменные  env: . Это эквивалент SQL-инъекций для GitHub Actions.
    •  Сохранение учетных данных (Credential persistence): установите  persist-credentials: false  для  actions/checkout , чтобы токен воркфлоу не оставался в  .git/config  для чтения каждым последующим шагом (или утекшим артефактом).
    •  Непривязанные экшены (Unpinned actions): закрепляйте каждый экшен за полным SHA коммита, указывая тег в качестве комментария. Плавающий тег вроде  @v4  может быть перенаправлен на вредоносный код любым, кто скомпрометирует экшен, — именно так атака на  tj-actions/changed-files  затронула 23 000 репозиториев. Позвольте Renovate или Dependabot поддерживать актуальность SHA.
    •  Чрезмерно широкие разрешения: установите  permissions: {}  на уровне воркфлоу и предоставляйте минимальные права для каждого джобы, снабжая каждый грант комментарием с пояснением. Установите базовый уровень и для всего репозитория: в разделе Settings → Actions → General → Workflow permissions выберите режим «только чтение» (read-only), чтобы воркфлоу, забывший объявить  permissions: , не получал по умолчанию права на запись в ваш репозиторий.
    •  Ненужные сторонние экшены: если встроенные инструменты раннера (например, CLI  gh ) могут справиться с задачей, откажитесь от обертки-экшена. Каждый экшен — это дополнительный код, работающий рядом с вашими секретами, и еще одна точка отказа выше по цепочке, которую могут скомпрометировать, чтобы добраться до вас.

    Вам не нужно запоминать этот список: запустите zizmor против  .github/workflows , исправьте найденное и добавьте утилиту в CI, чтобы регрессии отлавливались немедленно. Подробный разбор с примером эксплойта и его исправления для каждой уязвимости можно найти в статье Усиление безопасности воркфлоу GitHub Actions. Будьте особенно осторожны с триггерами  pull_request_target  и  workflow_run , которые запускаются с разрешениями базового репозитория против вкладов из форков.

  2.  Запускайте  composer audit  в CI, чтобы узнавать об уязвимых зависимостях из вашего пайплайна, а не от пользователей, и знайте, что с актуальным Composer резолверы ваших пользователей блокируют затронутые уязвимостью и помеченные как вредоносное ПО версии ваших зависимостей тоже (§6). Добавьте статический анализатор (PHPStan или Psalm) на уровне, который хотя бы ловит классику: невалидируемые входные данные, попадающие в запросы, пути к файлам или  unserialize() .

 Вырабатывайте привычки

  1.  Держите изолированное окружение для воспроизведения готовым (§4), чтобы «поднять виртуалку» было двухминутной рутиной, а не поводом пойти по опасному сокращенному пути.
  2.  Относитесь к отчетам о безопасности как к обычной категории работы, используя простой процесс из этого руководства, а не как к чрезвычайным ситуациям. Чем спокойнее рутина, тем лучше проходит каждый отдельный случай.
  3.  Знайте, кому позвонить. Что подводит нас к заключительному разделу.

10. Получение помощи

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

Дополнительные материалы


Если что-то из этого было неясно, потребовало слишком много времени на раздумья или на практике оказалось неверным, пожалуйста, сообщите нам; эта обратная связь напрямую улучшит опыт работы следующего мейнтейнера. Вы можете связаться с нами по адресам из §10.