conflict 字段在 install/update 时主动拦截不安全旧包,但不清理已安装版本;中文镜像不绕过 conflict 校验,lock 文件的 content-hash 和 shasum 才是安全锚点;CI 应禁用插件、脚本和 dev 包。

conflict 能拦住不安全旧包,但只在 install/update 时生效
Composer 的 conflict 字段是唯一原生支持的“主动拒装”机制,它不是运行时检查,而是在依赖解析阶段就报错中断。比如你明确知道 monolog/monolog 的 1.25.0 有 RCE 漏洞,就在 composer.json 根级写:
"conflict": {
"monolog/monolog": ">=1.0.0, <1.26.0"
}
注意:< 和 > 必须用 HTML 实体编码( / <code>>)或直接写成英文单词 lt/gt,否则 JSON 解析失败;逗号分隔多个约束,不能用空格或 &&。
这个配置对中文镜像完全透明——镜像只加速下载,不绕过 conflict 校验。但要注意:它不清理已安装的旧包,已有 vendor/monolog/monolog 不会自动删掉,得手动 composer remove monolog/monolog 再重装。
镜像本身不验证包安全性,lock 文件才是可信锚点
中文镜像(如阿里云、腾讯云)只是把 packagist.org 的元数据和 dist 包缓存一份,不做签名验证或漏洞扫描。真正防住“下错包”的,是 composer.lock 文件里的 content-hash 和每个包的 dist.shasum。
-
composer.lock必须提交进 Git,且不能出现在.gitignore中 - CI 流水线执行
composer install前,要先校验 lock 文件完整性:composer validate --strict - 如果有人手动改了
composer.json但没跑update就 commit lock,会导致实际安装版本与声明不符——这种“伪 lock”比没 lock 更危险
secure-http=true 不等于包安全,但它能拦住 HTTP 源注入
secure-http=true(默认开启)强制所有源必须走 HTTPS,并校验 X-Content-Signature 头,但它不检查 dist 包内容是否被篡改,也不阻止私有源绕过校验。
常见误操作:
- 设
composer config -g secure-http false→ 允许 HTTP 源,中间人可替换 packages.json - 加了自定义
repositories但没显式配type: composer和url→ Composer 自动放弃官方源校验,所有包都从私有源拉,哪怕该源根本没托管对应包 - 用了
fxp/composer-asset-plugin类插件 → 它们通常绕过主镜像配置,需单独配asset-packagist镜像,否则仍走原始 GitHub
安全底线:全局执行 composer config -g repo.packagist '{"type":"composer","url":"https://packagist.org"}',再单独切镜像,别留空 repositories 数组。
CI 中禁用插件和脚本,切断供应链攻击链
很多不安全行为发生在 install 阶段:恶意插件执行 hook、下载远控脚本、注入后门。这些不经过 conflict 或 lock 校验。
生产部署和 CI 必须加:
-
--no-plugins:禁用所有插件(Composer 2.2+ 默认禁止,但显式加更保险) -
--no-scripts:跳过post-install-cmd等钩子 -
--no-dev:不装require-dev里的包(它们常含调试工具类高危组件)
例如 GitHub Actions 中写:composer install --no-plugins --no-scripts --no-dev --prefer-dist。注意 --no-plugins 必须紧贴子命令,composer install --no-plugins 有效,composer --no-plugins install 无效。
真正容易被忽略的是:即使用了中文镜像,只要插件没禁,攻击者仍可通过污染插件仓库或利用插件漏洞完成注入——镜像只管“下载快”,不管“谁在下载后干了什么”。


















