composer install 才算真正锁定依赖,因其严格按 composer.lock 中记录的精确版本、子依赖及扩展安装,是 Composer 的强制契约;常见错误如 CI 中执行 update、gitignore 忽略 lock 文件或本地误删 lock 后 install,均会破坏锁定;正确做法是确保 lock 文件存在且最新,部署固定用 composer install --no-dev --no-interaction --no-progress,并禁用生产机上的 Composer。

依赖安全锁定不是“加个锁文件”就完事,而是靠 composer.lock + 严格安装流程 + 安全审计三者咬合才真正生效。
为什么 composer install 才算真正锁定依赖
只要 composer.lock 存在且没被绕过,composer install 就会 100% 还原其中记录的每个包、每个子依赖、甚至扩展(如 ext-json)的精确版本——这不是“大概率”,是 Composer 的强制契约。
常见错误现象:
- CI/CD 脚本里写了
composer update,每次构建都拉新 patch 版本,composer.lock形同虚设 -
.gitignore里误加了composer.lock,导致团队成员composer install实际走的是update逻辑 - 本地删了
composer.lock后直接composer install,等同于全量重解析依赖树
正确做法:
- CI 流水线第一步必须
ls -la composer.lock确认文件存在且时间戳最新 - 部署命令固定为
composer install --no-dev --no-interaction --no-progress - 禁止在生产机上装 Composer 或执行任何
composer命令
composer audit 是怎么拦住不安全包的
composer audit 不是扫描已安装代码,而是比对当前 composer.lock 中所有包的版本与官方安全咨询库(Security Advisory Database)。它能立刻发现已被标记为 CVE-2025-xxxx 的包。
关键参数差异:
-
composer audit:只报告,不中断 -
composer audit --fail-on-warnings:CI 中推荐,有中危及以上就让构建失败 -
composer audit --format=json:方便脚本解析,集成进流水线
注意:roave/security-advisories 这类包必须放 require-dev,它不提供运行时代码,只在 install/update 阶段做冲突拦截——放错位置会导致生产环境报错。
哪些操作会悄悄破坏锁定效果
看似无害的操作,实际等于主动弃守安全边界:
- 执行
composer update vendor/package-name后没提交新的composer.lock→ 别人install还是旧版,漏洞照旧 - 用
composer require vendor/package:dev-main引入不稳定分支 →composer.lock会记录 commit hash,但该 hash 可能随时被 force push 覆盖 - CI 缓存了旧的
vendor/目录,又没清缓存就跑composer install→ 实际加载的是缓存里的旧包 - 配置了
"config": {"platform": {"php": "8.1.0"}},但composer.lock里写的是"php": "8.1.10"→ 平台约束不一致,可能导致ext-curl等扩展行为漂移
生产环境部署前必须验证的三件事
锁定本身不保证安全,只保证可重现。上线前要确认:
- 运行
composer show --outdated --direct,看核心运行时依赖(如symfony/http-kernel、doctrine/dbal)是否已知有高危更新 - 执行
composer audit --fail-on-warnings,确保没有未处理的安全告警 - 用
composer why-not vendor/package:latest检查有没有某个老包在死锁关键升级路径(比如laravel/framework卡在 v9,导致无法升monolog/monolog到 v3)
最常被忽略的一点:安全锁定的有效性,取决于你是否真正在预发环境跑过完整业务链路。一个被 audit 放过的包,可能在特定配置下触发未公开的 RCE —— 锁定版本只是起点,不是终点。











