每一刻,都值得记录
← 返回首页

重访包管理器威胁模型

文章封面

重访包管理器威胁模型

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


感谢读到这里。

留下一点回声

MY LITTLE COLLECTION

本地收藏夹

仅保存在当前浏览器,不会同步到其他设备。清理网站数据会删除收藏与阅读记忆。

QUICK ACCESS · 快捷指令

↑ ↓ 选择 · Enter 执行 · Esc 关闭

MAKE YOURSELF AT HOME

阅读与场景

阅读外观

应用于文章和独立页面,偏好仅保存在当前浏览器。

季节场景

系统减少动态效果、后台标签页及沉浸阅读时自动暂停。

本地数据备份

备份收藏、阅读进度和外观偏好。文件不上传服务器,仅支持同站点备份;导入时合并数据。