Composer 2.10 正式发布,内置恶意软件拦截与统一依赖策略管理
Composer 2.10.0 正式发布,引入原生恶意软件过滤机制,并提供统一、面向未来的可定制依赖策略配置,用于集中管理安全公告、废弃包以及新增的恶意软件的处理行为。Packagist.org 上发布的软件包将通过 Aikido 获得快速恶意软件检测能力。
本次发布是 Composer 与 Packagist 推动供应链安全改进工作的一部分,相关背景已在官方博客昨天发布的文章中进行了阐述。
软件供应链面临持续攻击
开源软件包生态系统已成为供应链攻击中日益频繁且具有吸引力的目标。尽管形近字攻击(typosquatting)与依赖混淆攻击多年来一直受到关注,但近年来,带有明确恶意意图的软件包数量持续攀升。这些包携带凭据窃取程序、加密挖矿脚本或后门,其攻击目标往往是开发者的本地机器,而非生产系统。安全防护需要在代码审查和生产部署之前更早介入——即在开发者首次安装依赖或更新依赖版本的那一刻。
Composer 此前已经能够过滤公共数据库中标记为存在已知漏洞的软件版本,但安全公告通常覆盖的是对合法软件包中已披露漏洞的通报。公告发布流程往往需要数天时间,不适合对完全属于恶意软件的包——或通过凭据窃取发布到 GitHub 仓库的单个恶意版本——做出快速响应。就在几天前,攻击者通过某位开发者的机器获得了 GitHub 仓库的访问权限,并向 laravel-lang 系列软件包发布了恶意代码。两周前,热门包 intercom/intercom-php 在 packagist.org 上被攻击者发布了恶意版本,该攻击者同样获取了底层 GitHub 仓库的访问权限。正是依靠快速检测和及时的人工干预,Packagist 方面才避免了最严重的后果。
除了本次发布公告,Composer 官方博客昨天还发布了一篇关于 Composer 与 Packagist 供应链安全现状的深入分析文章。
恶意软件策略
Composer 2.10 引入了恶意软件策略,以便快速剔除恶意版本——无论这些版本是由短暂获得合法包控制权的攻击者发布的,还是由行为不端的维护者发布的。被标记的版本将从依赖解析池中移除,因此无法通过 composer update、composer require 或 composer create-project 进行安装。更为关键的是,该检查同样在 composer install 期间执行:如果在 composer.lock 生成之后某个版本才被标记为恶意,则下一次 composer install 将会失败。这意味着,一旦恶意版本混入了锁定文件,它不会在 CI 运行或生产部署中被悄然拉取。相同版本还会被 composer audit 检出,并且在发现恶意软件时默认导致审计失败。
得益于 Aikido 以 CC-BY 4.0 许可向 Packagist.org 提供的、包含其标记为恶意的软件包版本的数据源,该功能默认对所有 Composer 用户启用,从 Packagist.org 安装软件包无需任何额外配置。版本被标记为恶意的软件包会在 Packagist.org 对应的包页面上显著标注,便于浏览时识别。
Private Packagist 已经支持针对恶意软件的新依赖策略,客户可以确保其应用获得同样的额外安全保障。Private Packagist 的更新审查功能(Update Review)现在除了安全公告之外,还会高亮显示恶意软件。团队正在快速扩展安全监控能力,在安全公告处理之外,并行扩展恶意软件检测能力。恶意软件通知将在当前已启用安全公告通知的所有渠道中同步开启。Composer 团队将在未来几天内发布后续文章,介绍 Private Packagist 基于本次 Composer 发布构建的新供应链安全功能。

该功能最初在 PR #12766 中实现,并在 PR #12804 中整合到新的策略配置体系下,具体讨论见 issue #12786。
默认依赖策略
默认设置符合大多数项目的开箱即用需求:
- 恶意软件在更新期间被阻止,审计时判定失败,并且在
composer install期间被阻止,以尽可能减少对系统的影响。 - 带有安全公告的软件包版本在更新期间被阻止,审计时判定失败,但仍然可以安装。开发者有机会在决定修补之前,先评估已披露的漏洞是否真正影响自身项目。
- 废弃包仅在审计时报告,不会在更新或安装期间被阻止。
依赖策略配置
Composer 2.10 将安全公告、废弃包以及新增的恶意软件版本的处理配置统一整合到 config.policy 对象下。这一新配置取代了此前的 config.audit 设置。
每种依赖策略配置共享相同的结构:
block控制在composer update、composer require等操作执行之前,是否将匹配的软件包版本从依赖解析池中移除;对于恶意软件而言,还控制是否允许从已有锁定文件中通过composer install安装对应版本;audit控制composer audit如何处理匹配的软件包版本(ignore、report或fail);ignore提供按包粒度的豁免规则。
此外,安全公告策略(advisories)支持通过 ignore-id(例如 CVE 编号)和 ignore-severity 豁免特定问题;恶意软件策略(malware)则支持通过 ignore-source 进行豁免。
恶意软件策略允许通过 block-scope 选项控制阻止行为的作用范围:是仅在 composer update(包括 require 和 remove)期间生效(与安全公告或废弃包策略一致),还是在 composer install 期间也生效,默认值为 all,即在所有场景下均生效。
可以通过将某项策略设置为 false 来单独关闭它:
{
"config": {
"policy": {
"abandoned": false
}
}
}将 "policy" 设置为 false 是一次性关闭所有策略的总开关。如果仅希望临时禁用依赖解析时的版本阻止或安装时的恶意软件版本阻止,可以使用新增的 --no-blocking 标志(或在 CI 环境中使用 COMPOSER_NO_BLOCKING 环境变量)。该标志适用于 update、require、remove、create-project 以及 install(仅对恶意软件生效)命令。
除了三种内置的依赖策略,组织还可以接入自定义策略。例如,接入一份由内部维护的、根据公司策略禁止使用的软件包版本列表。自定义策略依赖的软件包版本列表可以来自声明了该列表的 Composer 仓库,也可以来自通过 sources 配置的一个或多个显式 HTTPS 端点:
{
"config": {
"policy": {
"company-policy": {
"sources": [
{"type": "url", "url": "https://acme.example.com/bad-pkgs.json"}
],
"audit": "fail"
}
}
}
}对于已经配置了 config.audit.* 的项目,这些键在 2.10 中继续作为回退机制生效,Composer 2.11 将发出明确的弃用警告。迁移是一一对应的,例如 audit.block-insecure → policy.advisories.block,audit.ignore-severity → policy.advisories.ignore-severity。完整映射关系请参阅配置文档。
该工作通过多个 PR 交付,并汇总在 issue #12786 中。完整的策略文档可在 getcomposer.org 上查阅:Config。
全新赞助计划
Aikido 不仅免费向 PHP 生态系统提供了恶意软件分析数据,还成为了 Composer 与 Packagist.org 的财务赞助商,在此致以诚挚的感谢。资金支持帮助支付 Packagist.org 团队的员工薪酬,以保障 Packagist.org 的持续运营和维护——包括在深夜响应供应链攻击,以及持续维护和开发 Composer 的新功能。如果您的企业也有意加入这一全新赞助计划,请发送邮件至 sponsoring@packagist.org,以便纳入六月即将发布的启动公告和全新赞助网站。
弃用源码回退机制
配合新的恶意软件控制功能,Composer 2.10 将源码回退(source fallback)行为标记为弃用。此前,Composer 会在制品(dist)下载失败时尝试从源码安装软件包。这一意外的源码检出行为在从私有 Composer 仓库安装软件包时可能带来意料之外的安全隐患。例如,当 Private Packagist 已镜像了开源软件包,但由于网络配置导致连接失败时,源码回退可能导致从底层 Git 仓库安装一个包含恶意软件的匹配标签,而 Private Packagist 的制品中原本仍保留着已知的安全版本。
新增的 source-fallback 配置选项允许用户重新启用旧的源码回退行为。Composer 2.11 将移除此功能,如果确有依赖,请在 GitHub Issue Tracker 中提交 issue 说明具体使用场景。实现在 PR #12698 和 PR #12885 中。
--with 约束的通配符支持
composer update 的 --with 标志允许在单次更新运行中施加临时版本约束覆盖(在现有约束基础上进一步收窄),而无需修改 composer.json。在 2.10 中,--with 中的包名参数现在接受通配符,从而可以一次性收窄整个 vendor 命名空间下所有包的约束:
composer update --with "acme/*:^2.0"此前,这需要为每一个单独的包提供独立的 --with 参数。对于正在将项目迁移到框架或 SDK 新主版本(包含众多子包)的场景,这是一项显著提升效率的改进。
该功能在 PR #12658 中实现。
create-project 支持添加额外依赖
create-project 命令现在接受 --require 参数,允许在搭建新项目的同时引入额外依赖:
composer create-project acme/skeleton my-project --require="acme/extra-bundle:^1.0"这消除了在脚手架生成之后立即执行 composer require 的二次步骤,在以编程方式或在自动化流水线中创建项目时尤为便利。
该功能在 PR #12738 中实现。
其他值得关注的改进
--bump-after-update修复:该标志现在仅提升实际被更新的软件包的版本约束,而非锁定文件中的所有包。#12733- 插件自动加载优化:不再为每个插件的每个包单独重新生成类映射(classmap),从而减少安装和更新操作期间的开销。#12696
- PoolOptimizer 内存使用降低。#12783
完整的变更日志中可查阅更多功能与错误修复详情。