Composer如何验证依赖包的完整性?(签名与哈希校验)

2次阅读

composer install 无报错但包内容错误,因默认不强制校验完整性:仅首次安装或 –no-cache 时比对 dist.sha256,且缓存存在则跳过;镜像替换、本地缓存污染、source 安装均绕过校验。

Composer如何验证依赖包的完整性?(签名与哈希校验)

composer install 时为什么没报错,但包内容却不对?

因为默认情况下 Composer 不强制校验包完整性,它只比对 composer.lock 中记录的 dist.sha256(或 sha1)与下载后文件的哈希值——但仅限于从 Packagist 下载的 dist 包(zip/tar),且仅在首次安装或 composer install --no-cache 时触发。如果缓存中已有文件,Composer 会直接复用,跳过哈希比对。

  • 本地缓存污染(比如手动改过 vendor/ 或缓存目录被篡改)会导致校验失效
  • Packagist 的 dist 包若被镜像源替换(如国内某些镜像未同步签名),哈希可能匹配但内容已非原始作者发布
  • source 安装方式(如 "type": "package"path repo)完全不走哈希校验

如何启用强制哈希校验并验证现有 vendor?

Composer 本身没有「全局开关」来让每次 install/update 都重新计算并比对哈希,但可通过组合命令逼近效果:

  • 清空缓存再重装:composer clear-cache && composer install --no-cache,此时会重新下载 dist 包并校验 dist.sha256
  • 手动校验当前 vendor/:用 composer show --locked --format=json 提取每个包的 dist.sha256,再用 shasum -a 256 vendor/{package}/composer.json(注意:实际校验的是 dist 包解压后的根目录哈希,不是单个文件;官方不暴露校验逻辑细节,所以无法 100% 手动复现)
  • 更可靠的做法是使用 composer validate --strict 检查 composer.jsoncomposer.lock 结构一致性,但它不校验文件内容

签名(Satis / private Packagist)和哈希哪个更可信?

哈希(如 sha256)只能防意外损坏或传输错误;签名(如 GPG)才能防恶意篡改——前提是你的 Composer 配置信任该签名公钥,并且源本身支持签名分发。

  • Packagist.org 不提供包签名,只提供哈希,所以你依赖的是 Packagist 服务器自身的安全性
  • Private Packagist 支持 GPG 签名验证,需在 composer.json 中配置 "signature" : true 并导入对应公钥
  • Satis 不内置签名机制,需自行在生成仓库时用 gpg --clearsign 签署 packages.json,再配合自定义脚本验证
  • 签名验证失败时 Composer 会抛出 InvalidSignatureException 错误,而哈希不匹配通常只警告或静默跳过

CI/CD 中怎么防止依赖被悄悄替换?

靠本地 composer install 默认行为不够。关键是在构建流程中切断缓存干扰、锁定来源、并做二次校验:

  • CI 中始终使用 composer install --no-cache --prefer-dist --optimize-autoloader,避免复用不可信缓存
  • composer.lock 提交进 git,并在 CI 中加一步校验:git diff --quiet composer.lock || (echo "lock file changed unexpectedly"; exit 1)
  • 对高敏感项目,可额外运行 composer show --locked --format=JSON | jq -r '.packages[] | select(.dist) | "(.name) (.dist.sha256)"',与上一次构建的哈希快照比对
  • 别忽略 require-dev——它们同样参与 autoloader 生成,一旦被污染,测试环境可能掩盖运行时问题

哈希校验不是一劳永逸的开关,它依赖下载路径、缓存状态、以及 Packagist 自身是否被中间人劫持;签名才是真正的信任锚点,但目前绝大多数 PHP 项目根本没接入这层。

text=ZqhQzanResources