你不应该更新你的依赖项
速览
本文探讨了软件工程中依赖管理的常见误区,指出开发者往往过度追求最新版本而忽视了稳定性。盲目更新依赖可能引入不兼容的变更或安全漏洞,破坏现有系统的可靠性。作者建议采取更谨慎的策略,仅在必要时进行更新,并充分测试以确保系统稳定。
AI 深度解读
你不应该更新你的依赖:现代软件供应链信任的崩塌
背景
回顾 1999 年 1 月,那是一段相对简单的时光。一张罕见的历史照片捕捉到了一位系统管理员(SysAdmin)的身影——这一古老物种后来演变成了现代的 DevOps 工程师。照片中,这位标本正怀着对 Linux 2.2 发布的兴奋以及对即将到来的 LinuxWorld Expo 的期待,在生产线环境中进行半年一次的软件打补丁仪式。他身边伴随着他的“专性互利共生者”(通俗地说,就是“软件厂商的销售人员”)。
作者回忆道,自己在 90 年代末辍学后进入科技行业,第一台物理服务器在两周内就被入侵了(是的,是因为 phpMyAdmin,是的,因为未打补丁,是的,至今仍感羞愧)。在那个时代,我们深深内化了第一条准则:“非常仔细地审查你所依赖的内容,阅读所有变更日志和补丁,及时应用,始终保持最新。”
这种观念在当时听起来或许有些陈旧,甚至对大多数热衷于触发 npm 依赖自动更新(dependabot-trigger-happy)的人来说显得格格不入。然而,面对近年来席卷而来、看似不可阻挡的毁灭性供应链攻击事件,一些包管理器开始建议:在特定天数之前不要更新依赖项(仅仅是为了确保,你知道的,那些冲在你前面的“傻瓜”先付出代价并发现问题……)。
曾经被视为基本软件安全卫生和常识性智慧的做法——“不要更新得太快,以免暴露于持续的供应链攻击中”——现在被视为有害。当然,不升级也会让你暴露在对(技术上已修补的)上游 CVE 的主动攻击中。进退维谷。
核心内容
旧操作模式的终结与开源的悖论
旧的操作模式在一个更小、更简单的科技世界中确实行之有效。那是一个环境更受控、更孤立的时代,你只依赖少数几个可以手动审核的正式定义的供应商,复杂的供应链问题和庞大的依赖列表……甚至还不是科幻概念。
然而,过去两到三十年间向开源的大规模转变(部分得益于更好的安全叙事:“你受益于比闭源供应商更好的社区驱动审查!”……哦,天真无邪的孩子……),加上生态系统规模的指数级增长,暴露了巨大的新问题。这些问题通过一系列令人毛骨悚然的“核心依赖”漏洞痛苦地显现出来(还记得 bind、openssl,或者仅仅是 log4j 吗?),这些漏洞反复击穿了整个互联网。
维护者的困境与行业的盲目
第一次“觉醒”大约发生在 2010 年代中期:开源维护者不仅仅是免费劳动力,他们也是过度劳累、装备不足、人员严重短缺的,其能力与其他任何人一样参差不齐。然而,他们的工作支撑着市面上大多数软件(并且至今仍在支撑)。
加上包管理器本身根深蒂固的缺陷和天真,软件分发类别的不足,某些新(非常)流行技术栈的不称职,以及企业界加速推出竞争对手的压力,共同孕育了一种文化。这种文化保留了先前文化的表象(“始终保持完全修补”的部分),但已 devoid of substance(空洞无物),本质上变成了“不管是什么,统统更新,甚至不看一眼”。
供应链漏洞到供应链入侵:信任的终结
“供应链漏洞”的概念变得主流。行业的广泛反应大多是两手一摊:“我们用的是最新版本。上游会修补。你还想让我们做什么?”
前卫的做法则是参与 nicely packaged red-tape bureaucracy(包装精美的官僚主义),这并没有解决根本问题,只是通过创造额外工作(当然,为了收取一笔不错的费用)让我们感觉良好:负责任披露计划、宏大的安全政策声明、禁运、CVE 以及神圣的 CVSS(超级有用,对吧?RIGHT?)、合规流程、认证……
或者用更生动的语言来说:“也许 OpenSSL 会有更多问题,但我们不知道如何修复它,更不用说重写它了。我们不知道里面到底用了什么,即使知道,我们也没有时间去做,而且这比偶尔的全球灾难处理起来成本更高。所以,让我们继续盲目地汲取他们发布的任何东西吧,反正我们会和世界其他部分一样(不)安全。哦,嘿,我们通过了 ISO 认证。”
这是一种扭曲版本的“数量即安全(只要我不需要做任何事)”,是对原始开源理想主义承诺(互利社区)的扭曲。
在你声称你从未是我们中的一员之前:看着我的眼睛发誓你向你的 OSS 上游捐赠了,并且你从未在未查看 diff 的情况下合并过 Dependabot PR。
你没有。而且你也确实合并过。
从供应链漏洞到供应链入侵:信任的终结。读者此时应该显而易见,或者任何经历过过去十年的人(或者任何从……1984 年以来一直关注的人)都知道,行业本身邀请了下一次升级。如果“供应链”只是被大多数人盲目信任而没有进行任何实际有用的验证,那么为什么要费力去寻找现有链接中的漏洞呢?当更容易、更便宜的方法是入侵开发者账户(或工具)并发送武器化的版本时?
现代 AppSec 的本质
在 2020 年代中期,安全不再(那么)关乎保护你自己的软件(对于大多数行业来说,现代框架和语言今天处理了你在 90 年代必须与之斗争的大多数安全荆棘,渗透测试、审计和静态分析也是成熟的做法)。
这也更少关乎修复上游依赖项代码中的问题(虽然作为一个一般性问题远未解决,但重要的核心基础设施部分(DNS、TLS)在很大程度上已经清理了他们的行为,这要归功于行业上层半心半意的支持,而对于其他一切,我们基本上放弃了,只是在禁运期间等待上游修补,一边做填字游戏一边检查合规清单)。
那么,现在 AppSec 主要关乎什么?
它关乎无法完全信任供应链本身。
(如果你需要名字,仅过去 12 个月:Shai Hulud, Nx s1ngularity, axios, TeamPCP, chalk / debug / qix, tj-actions/changed-files)
讽刺的是,我们现在在技术上更好地配备了正确处理供应链加固的能力(按特定顺序排列:加密传输不再是可协商的,内容寻址,OIDC / Fulcio, TUF, SLSA, 2FA 等)。或者也许正是因为这一点,由于正确使用所有这些内容所需的额外复杂性和努力,在整个行业中采用最佳实践、更好的默认值以及对更好工具投资的合理性……令人失望。
无论多么好,来源证明(Provenance)如果不被消费,其绝对价值为零。无论 merits or value( merits 或价值如何),使用需要努力的硬性技术解决方案来保护供应链,其成功都出奇地难以实现。
如果你需要说服:
今天进行软件开发的公司在其堆栈中有多少比例使用了 FROM registry.whatever/debian:latest?
为了记录在案,我在 2014 年领导了 Docker 分发团队,从底层重建容器镜像分发,基于内容寻址,故意提供用于容器化软件分发的安全原语。这似乎是一个理想的技术解决方案,但也导致了巨大的可用性挫折,导致几乎每个人都使用这些软标签(soft-tags)。
有多少人不将他们的 GitHub Actions 固定到 sha,并且不理解这意味着什么?这甚至合法吗?有多少人做了,但仍然愉快地合并 Dependabot PR,只是将其 bump 到当前可用的任何版本,并因此感到“安全”?
有多少人盲目地执行 go install github.com/someorg/[email protected],心想“这没问题”?
“go 是安全的”,“域名是 github.com”……
关键要点
- 依赖更新策略的逆转:传统的“始终保持最新”的安全卫生准则在面对现代供应链攻击时已失效。现在,延迟更新依赖项以观察上游是否出现安全问题,反而成为一种被部分包管理器推荐的防御策略。
- 开源维护体系的脆弱性:开源维护者普遍面临过度劳累、资源不足的问题,其代码质量参差不齐。然而,整个软件行业严重依赖这些维护者的工作,形成了巨大的系统性风险。
- 合规主义的虚伪性:行业通过引入复杂的官僚流程(如 ISO 认证、CVSS 评分、负责任披露计划)来制造“安全可控”的幻觉,但这些措施并未解决供应链信任的根本问题,反而增加了开发负担。
- 信任机制的崩塌:由于缺乏有效的验证机制,攻击者发现入侵开发者账户或工具比寻找漏洞更廉价、更高效。供应链攻击已从“利用漏洞”演变为“直接入侵与投毒”。
- 技术解决方案的落地困境:尽管存在 TUF、SLSA、内容寻址等先进的供应链安全技术,但由于复杂性高、采用门槛高,行业整体采纳率极低。
