恶意 Rust crate Arrayref 运行了构建时载荷
一个最近被入侵的 Rust crate `arrayref` 使用恶意构建时脚本在开发者机器上执行载荷,再次引发了对超越 JavaScript/npm 世界的软件供应链攻击的担忧。评论者争论 Cargo 默认运行任意 `build.rs` 和 proc-macro 代码应承担多少责任,并提出了诸如构建沙箱化、强制最小发布年龄、更好的审计工具以及精选或“受祝福”的库等缓解措施。该事件还引发了关于依赖文化和标准库设计的更广泛争论,一些人主张采用“开箱即用”的生态,以减少对庞大、难以审查的依赖树的依赖。
攻击做了什么,以及为什么这令人担忧
- 一个受欢迎的小型 crate 通过其维护者账号遭到入侵;新版本添加了恶意构建脚本和一个 proc-macro 依赖。
- 在 Windows 上,构建脚本下载了一个远程载荷,将其写入临时 PowerShell 脚本,并通过 VBScript 启动器执行,以逃避 Cargo 的作业对象。
- 一些评论者指出,这主要针对开发者/CI 机器(以及其中的密钥和云凭据),而不是终端用户的运行时环境。
构建时威胁 vs 运行时威胁
- 许多人认为构建脚本和 proc macro 尤其危险,因为它们会在构建过程中自动运行,通常早于代码审查。
- 也有人反驳说,攻击者可以把载荷移到普通库代码中,并在测试或运行时触发,因此只关注
build.rs并不完整。 - 不过,仍有人认为构建时通常比运行时能接触到更广泛的秘密,值得更强的加固。
Cargo、crates.io 与事件响应
- 恶意版本在大约 1.5 小时内就从 crates.io 被移除(不是仅仅 yanked);一些人称赞了这个速度。
- 也有人批评用户体验:被删除的版本会从版本列表中消失,早期也没有可见的公告,而且用户被要求运行临时的
find命令,而不是使用内置的cargo审计路径。 - 关于 crates.io 是“准备不足”还是在志愿者约束下已尽力而为,也存在争论。
缓解措施与工具
- 强烈支持以下做法:
- 设置最小发布/升级年龄(
min-publish-age),以避免新鲜的恶意发布。 - 更好的默认设置:阻止或显式白名单化
build.rs/proc-macro,并在依赖首次添加它们时大声警告。 - 对构建脚本(有时也包括测试)进行沙箱化,限制文件系统访问并禁止网络,不过有人认为跨平台沙箱很难实现,也容易被绕过。
- 设置最小发布/升级年龄(
- 提到的现有工具:cargo-deny、cargo-vet、cargo-crev、
min-publish-age(nightly)、离线构建、vendoring、外部沙箱(bwrap、Landlock 等)。
stdlib 大小、依赖文化与生态系统对比
- 围绕 Rust 的大量小型 crate 与 Go、.NET、Java、Apple API、Python 这类“开箱即用”标准库之间,展开了激烈讨论。
- 一些人认为,大型 stdlib 和经过精选的“受祝福” crate 能减少依赖膨胀和攻击面;另一些人则强调,大型 stdlib 也会停滞,而且同样存在漏洞。
- 与 npm/Node、Python、C++、Go 的比较:普遍共识是,任何拥有便捷依赖管理和大量微型包的生态都会面临类似的供应链风险;文化和筛选与语言设计同样重要。
更广泛的教训
- 许多人主张将开发环境容器化/沙箱化,并默认把新的或小众的 crate 视为不可信。
- 也有人提出基于能力或效果的语言/操作系统的理想化方案,但另一些人认为,这更多是一个社会和资金问题,而不只是纯技术问题。