开源软件的可持续性困境:谁在承担「免费」的成本
2024 年 3 月,一个叫 Jia Tan 的账号在 XZ Utils 里植入了后门。
XZ Utils 是一个 Linux 压缩工具库,几乎所有主流发行版都预装。这个后门经过精心设计,能在特定条件下劫持 sshd 的认证过程——影响范围是全球所有运行受感染发行版的 Linux 服务器。
让这件事成为分水岭的,不是攻击本身的技术复杂度,而是它揭示的一个问题:一个支撑全球基础设施的关键软件,只有一个精力已经严重透支的业余维护者。
依赖金字塔
npm 上有一个包叫 is-odd,判断一个数是否是奇数,核心代码只有一行。它每周的下载量长期维持在数百万次,被数以千计的包所依赖。
这不是笑话,是现实的缩影。
现代软件的依赖图是一座金字塔。顶端是你的应用;往下是几十个直接依赖;再往下,通过传递依赖,可能堆叠了几百甚至上千个包;在金字塔的底部,是一批维护者寥寥、甚至只有一个人在维护的基础库。
2022 年的一项分析发现,npm 生态里约 11% 的包只有一个维护者,而这些包的累计被依赖次数占整个生态的 30% 以上。你的生产系统很可能在某个角落依赖着一个不知名的开发者用业余时间维护的代码。
这不是 npm 特有的问题。Log4j 在 2021 年爆出的 Log4Shell 漏洞,让整个互联网陷入几周的紧张修补。Log4j 是 Apache 基金会的项目,算是相对有组织的开源——即便如此,在事件发生时,真正活跃的维护者只有两三个人,他们同时面对着几千个 Issue 和数十亿受影响的部署。
维护者在燃烧
开源软件的消费方式,和消费产品服务没有区别。用户期望 bug 被修,特性被添加,安全漏洞被即时响应,文档要清晰,迁移指南要齐全。
但开源项目的大多数维护者没有报酬,或者获得的报酬远低于他们的市场价值。他们在工作之余回复 Issue、审 PR、发布版本,承担着相当于初创公司产品团队的工作量,却在心理上还要应对那些语气不佳的用户和”为什么这个 bug 还没修”的催促。
“维护者倦怠”(maintainer burnout)在过去几年里被反复讨论,但讨论没有解决问题。
2016 年,left-pad 的作者 Azer Koçulu 因为一个商标纠纷,一怒之下把自己所有的 npm 包全部撤销发布。其中一个包被 React 和 Babel 依赖,撤销后导致全球大量构建系统在十分钟内崩溃。
这件事通常被讲成 npm 生态脆弱性的案例,但实际上它是一个维护者心理崩溃的案例。Koçulu 不是破坏者,他是一个觉得自己的劳动和权利没有被尊重的人,做出了一个极端的反应。
类似的事情在以更安静的方式持续发生:维护者默默停止更新、将项目归档、或者在没有任何通知的情况下转移到不活跃状态。这些项目的下游依赖者,往往是在几个月或几年后才意识到出了问题。
资金模式的现实
市场意识到了这个问题,出现了几种试图解决它的模式。
基金会模式:Linux Foundation、Apache Foundation、CNCF。把有商业价值的项目纳入治理框架,提供法律保护、基础设施支持、品牌背书,以及通过会员费分配给项目的少量资金。这个模式对头部项目有效,但基金会的资源有限,覆盖不了长尾。
赞助平台:GitHub Sponsors、Open Collective、Patreon。让个人和公司直接赞助维护者。这些平台确实有成功案例,但成功的维护者往往是已经有相当知名度的人——能从赞助中获得可观收入的维护者,和那些最需要资金支持的长尾维护者,不是同一批人。
商业开源:HashiCorp、Elastic、MongoDB 走过的路——开源吸引用户,企业版或云服务赚钱。这个模式在商业上可行,但在治理上有内在张力,最终往往导致 License 修改(BSL、SSPL),以及随之而来的社区分叉。Terraform 的 OpenTofu、Elasticsearch 的 OpenSearch,都是这条路走到一半时的分裂产物。
企业内部资助:Google 资助 Go 和 Kubernetes,Meta 资助 React,微软通过收购 GitHub 间接支持生态。这是目前最大的资金来源,也是最不稳定的来源——企业资助的优先级随商业利益变化,优先级可以在一个季度内被调整。
没有一种模式是完整的答案。
供应链安全把问题推到了台面
如果说 Log4Shell 让安全社区开始重视开源依赖的问题,XZ Utils 后门则让更广泛的技术界意识到:攻击者正在把维护者枯竭作为攻击面。
Jia Tan 的入侵方式不是代码漏洞,而是社会工程学。他在两年内通过高质量的贡献建立了信任,同时在社区内以各种方式向原维护者施加压力,最终获得了 commit 权限。整个过程有充足的耐心和资源,看起来不像个人行为。
这种攻击之所以有效,前提是目标项目的维护资源极度稀缺,正在寻找新的贡献者。维护者倦怠不仅是社会问题,也是安全漏洞。
对此,美国政府在 2022 年通过了 EO 14028,要求联邦软件供应商提供 SBOM(Software Bill of Materials,软件物料清单)。SBOM 是把软件依赖图显式化的标准,使企业和政府可以在已知漏洞出现时快速评估暴露面。这个方向是正确的,但 SBOM 只解决了可见性问题,不解决可持续性问题——你知道自己依赖了什么,但那个被依赖的项目还是只有一个维护者。
企业的角色
这里有一个结构性的讽刺。
Fortune 500 公司的技术栈依赖着数千个开源项目,这些公司的工程师每天都在使用这些项目,但直接资助这些项目的公司,是极少数。大多数公司的态度是:开源免费,用就是了。
部分公司会通过贡献代码的方式回馈,但贡献代码不等于维持项目的运转。发现并修复一个影响自己业务的 bug,和长期维护一个项目的 Issue 追踪、版本发布、文档更新、安全响应,是两件完全不同量级的事情。
一些大公司在这方面做得更系统。谷歌有内部项目追踪关键的开源依赖,评估其健康程度,必要时投入工程师资源或资金;微软有 OpenSSF(Open Source Security Foundation)的深度参与;亚马逊通过 AWS 上的托管服务间接资助了若干项目的维护。
但这些是例外,不是惯例。
“消费开源但不回馈”是市场失灵的经典案例——这是公地悲剧在软件世界的版本。每个单独的公司都有理性动机搭便车,但当所有人都搭便车,公地就会耗竭。区别是草地耗竭是缓慢的,软件维护者的崩溃可以在一天内发生。
从治理实践来看
做了几年开源团队的工作,一些反直觉的观察:
最健康的开源项目,往往不是最”民主”的。治理清晰、决策路径明确、有人真正负责,比投票多的委员会更能让项目走下去。BDFL(Benevolent Dictator for Life)模式有其问题,但至少有人在场。
贡献者数量不等于项目健康。一个有 500 个贡献者、但其中 480 个是单次提交 PR 修错别字的项目,和一个有 10 个长期活跃维护者的项目,生命力差距是天壤之别。真正有价值的是长期的、有深度的参与,不是 contributor 图表上的数字。
“好文档”是可持续性的代理指标。文档差的项目,知识集中在少数人脑子里,一旦这些人离开,项目就会快速空心化。花时间写文档不出名、不出成果,但它是社区传承能力的体现。
对外部贡献者说”不”是维护者最重要的能力之一。接受一个低质量的 PR,代价不是合并那一刻——是之后几年的维护、调试和兼容性保证。Feature request 的接受阈值越低,项目范围越扩散,维护者的精力就越分散。
没有银弹
这个问题没有可以宣布”解决了”的方案。
SBOM 让供应链可见,但不解决可持续;基金会提供框架,但资源有限;商业开源可以活下去,但会遭遇治理张力;企业赞助有效果,但不稳定。
更现实的路径,可能是这几件事同时推进:
让大型企业感受到不回馈的真实成本——Log4Shell、XZ Utils 这样的事件每隔几年就会有一次,每次都是一次推动。
把开源健康度量化并纳入采购和合规流程。SBOM 是开始,下一步应该是对依赖项的维护活跃度、贡献者多样性、安全响应速度设定最低门槛,就像我们对第三方服务商设定 SLA 一样。
让维护者的工作可见。GitHub 的 Insights、CHAOSS 的度量框架,都是在尝试把维护工作的价值量化。不可见的劳动很难获得支持。
这些不会根本解决问题,但会让崩溃的边界离我们更远一点。
开源软件是 21 世纪最成功的协作模式,也是最大的免费搭车现象之一。
这两件事同时为真。如何在它们之间维持一个不会崩溃的均衡,是整个行业在接下来十年里不得不认真对待的工程和治理问题。