← 返回内容列表

依赖管理反思:为什么应该直接从VCS获取依赖

依赖管理反思:为什么应该直接从VCS获取依赖

几乎所有 npm/RubyGems 供应链攻击都依赖"发布包"这一中间步骤。文章认为,Go 直接从 VCS 获取依赖的做法从根本上消除了这一攻击面,让依赖审计变得简单透明。AI 扫描工具无法替代模型层面的安全设计。

一位从 Go 转向 Ruby 的开发者在 Hacker News 上引发了热烈讨论。他的核心观点是:几乎所有"侧信道攻击"更准确地说是"包发布攻击"——它们依赖在"发布包"步骤注入恶意代码。而 Go 的做法从根本上消除了这一攻击面。

Go 的做法:直接从 VCS 获取

在 Go 中,依赖通过 URL 标识,如 github.com/user/pkg。Go 工具链识别使用的 VCS(通常是 git),获取 go.mod 中指定的 tag 或 commit。go.mod 同时充当依赖规范和锁文件——列出精确版本(没有 ~>1.1 这种范围),包含直接和间接依赖的完整树。

所有文件的哈希在 sum.golang.org 上核对,防止 tag 被替换。还使用代理防止仓库被"left-pad"式删除。

审计依赖更新非常简单:git log -p old..new,阅读所有提交,更新 go.mod。不需要深度审查,只需寻找可疑的东西——比如在 globbing library 中出现 exec.Command(..)http.Post(..) 会非常明显。

Go VCS直接获取 vs npm发布包模型对比

RubyGems/npm 的做法:发布包

Ruby 需要创建 .gem 归档并上传到 rubygems.org。包内容与源码仓库之间没有保证一致。审计一次更新需要:

curl -s https://rubygems.org/downloads/example-2.7.5.gem > old.gem
curl -s https://rubygems.org/downloads/example-2.8.2.gem > new.gem
mkdir old new
tar xf old.gem -C old; tar xf new.gem -C new
(cd old && tar xf data.tar.gz); (cd new && tar xf data.tar.gz)
diff -urN old new

这能工作,但远非简单。单个提交的信息丢失了,整体更难审计。在 diff 很大的情况下,没有提交历史是个大麻烦。

供应链攻击的常见模式

几乎所有的"侧信道攻击"更准确地说是"包发布攻击":

  • npm 攻击:获取 npm 账户权限,在发布包中注入恶意代码。源码仓库完全干净。
  • xz 后门:exploit 代码在源码仓库中存在但处于休眠状态(隐藏在二进制测试文件中),只在修改后的 .tar.gz 发布包中被激活。源码审计无法发现。
  • event-stream 事件(2018):添加了对 flatmap-stream 的依赖,恶意代码仅存在于 npm 发布包的 index.min.js 中,源码仓库中完全没有。

关键洞察:源码仓库被攻破的情况很罕见,因为太显眼了。攻击者需要至少稍微隐藏 exploit 才能有效。而"发布包"步骤提供了完美的隐藏机会——尤其是包含编译产物(minified files、二进制文件)的包。

供应链攻击常见模式时间线

第二重问题:编译产物

JavaScript 从 TypeScript 生成后,虽然不是完全不可读,但远不如原始 TypeScript 可审计。更不用说 minified 文件或二进制 blob 了。这在 Ruby 中问题较小,但在 npm 生态中是更大的问题——恶意代码可以藏在 .min.js 中,源码仓库中完全不存在。

AI 扫描不是解决方案

RubyGems 最近添加了"冷却"选项和"AI 辅助漏洞扫描"。作者认为这不是根本解决方案:

"AI 工具不会神奇地修复发布包模型缺乏透明度的问题。"

更合适的方案是重新审视整个"发布包"模型。Go 的做法虽然不是完美无缺,但就目前所知,这是最好的方案。

实践建议

Bundler 已经部分支持从 git 获取依赖:gem 'rails', git: 'https://github.com/rails/rails.git', tag: 'v8.1.2'。但间接依赖仍从 rubygems.org 获取。作者提出了一个概念性的改进方案——允许 Gemfile 声明"必须使用 git 获取所有依赖",并将依赖标识改为 URL 而非 gem 名称。

对于 Go 开发者来说,这些都不成问题。每次依赖更新只需 git log -p old..new——简单、透明、可审计。也许其他语言生态应该认真考虑这种做法。

[关联推荐]

评论 (0)

加载评论中…

依赖管理反思:为什么应该直接从VCS获取依赖 | 必学必会