重访包管理器威胁模型
文章目录
重访包管理器威胁模型
Package Manager Threat Model, Revisited — nesbitt.io · 14 小时前 · 🔒 安全

包管理器长期以来的威胁模型把注意力集中在依赖混淆、抢注(typosquatting)、维护者账号被劫持、postinstall 脚本执行任意代码以及注册表本身被入侵这几类风险上,而这次重访得出的判断是:真正被系统性忽略的入口是清单文件本身。package.json、pyproject.toml、gemspec、Cargo.toml、composer.json、pom.xml 这些用来描述「这个包是什么、装到哪里、暴露哪些文件」的元数据,几乎被所有实现当成可信输入,而审计的十个包管理器里有八个可以通过清单字段触发的路径穿越(path traversal)被攻破;在四个月的时间里,其他人公开的安全公告中同类问题出现了三十三次,这个比例说明它不是某个生态的偶发 bug,而是跨语言、跨注册表的系统性设计缺陷。攻击路径大致是这样:清单里的 bin、files、exports、main、types、entry_points,以及 lockfile 中记录的 tarball 地址和解包目标目录,会被安装器直接拼接进文件系统路径,一旦缺少规范化(normalization)和白名单校验,一个写成 ../../.ssh/authorized_keys 的字段值就能在安装阶段写到项目目录之外,覆盖 shell 启动脚本、CI 里的凭据文件、系统服务配置,或者把自己塞进下一次构建必定会执行的位置。更棘手的是,签名与来源证明(provenance)只回答「这是谁发布的」,不回答「解包时会写到哪里」:一个通过了 SLSA 校验、来自可信维护者的包,照样可以在清单里带上穿越路径,因此供应链安全的现有工具箱在这里是失效的。作者的结论是,包管理器必须把元数据当作代码来对待,把路径穿越归入安装器层面的一类漏洞去系统性修复,而不是继续当成个别包作者的粗心失误,同时把安装阶段默认放进沙箱、只读挂载和最小可写根目录里。
核心论点
- 审计给出的硬数据是:十个包管理器里有八个可以被清单字段触发的路径穿越攻破,而在四个月的时间里,第三方安全公告中同类问题出现了三十三次;这个命中率说明漏洞位于安装器的公共设计层面,而不是少数包作者的粗心,任何只针对单个包的补丁都只是在打地鼠。
- 具体的攻击面比想象的宽:bin、files、exports、main、types、entry_points、gemspec 中的 require_paths,以及 lockfile 里记录的 resolved URL 与解包目标目录,都会被拼进文件系统路径;除此之外还要考虑符号链接穿越、Windows 保留设备名、大小写不敏感文件系统的路径折叠以及 NTFS 备用数据流这些容易被校验逻辑漏掉的边界情况。
- 签名与来源证明解决不了这类问题:provenance 和 SLSA 保证的是「这个包由谁构建、经过哪条流水线」,而不是「解包时文件最终落在哪个目录」;一个签名完全合法、维护者身份可信的包,依然可以在清单里写入穿越路径,因此来源可信和内容安全必须当成两个独立的信任维度分别建模。
- 修复方向是把不可信输入的处理下沉到安装器:对清单里的所有路径字段做规范化并拒绝 .. 与绝对路径,解包时用沙箱、只读挂载和受限的可写根目录,在 CI 中隔离安装步骤并限制其对凭据文件的访问;核心思路是把「安装即执行」这一事实显式写进威胁模型,让路径穿越成为安装器层的一类漏洞而不是逐包修复的偶然事件。
💡 为什么值得读: 它用一个可复现的比例(十个里八个、四个月三十三次)把供应链安全的注意力从 postinstall 脚本拉回到被忽视的清单字段,并解释了为什么签名和 provenance 挡不住这类攻击。
🏷️ package-manager, supply-chain, path-traversal, security
留下一点回声