依赖树是社交网络,漏洞是传染病——Epidemiological Dependency Graph Propagation
依赖树是社交网络,漏洞是传染病
问”有多少包受了影响”是错误的问法。正确的问题是:谁是超级传播者?哪里是免疫缺口?R0 是多少?
传统 CVE 响应框架的失效
当一个零日漏洞(如 Log4Shell、XZ Utils)被披露时,标准的响应是:
- 确认受影响版本范围
- 列出所有直接依赖该库的包
- 发 patch / 升级版本
- 等待整个生态慢慢跟进
这个框架有两个根本缺陷:
缺陷一:静态快照,无视传播拓扑
传统分析回答的是”第一层有多少包受影响”,但 npm/pip 的依赖图是多层的、递归的。你的直接依赖的间接依赖的间接依赖,可能在第三层、第四层才触达漏洞。你以为自己没事,只是因为你还没走到那个深度。
缺陷二:没有优先级概念
“1000 个包受影响”和”3 个超级传播者包一旦修复,其他 997 个自动解除风险”是两个完全不同的情况,但 CVE 列表把它们平铺在一起。修复顺序决定了解除风险的效率。
流行病学框架的引入
这篇工作把 npm/pip/cargo 依赖图当成接触网络(contact network),把 CVE 当成病原体,借用流行病学的核心概念来建模供应链漏洞传播:
核心流行病学指标映射
| 流行病学概念 | 在依赖图中的对应 |
|---|---|
| R₀(基本传播数) | 一个受漏洞影响的包平均会通过依赖关系传染多少个其他包 |
| 超级传播者(Super-spreader) | 被大量其他包依赖的热门库(express、lodash、@babel/runtime) |
| 免疫缺口(Immunity gap) | 虽然没有直接漏洞,但依赖路径经过漏洞包的包 |
| 垂直传播(Vertical transmission) | 通过多层依赖链向下游传播 |
| R(t)(有效传播数) | 随时间变化的传播数,随着修复和升级而衰减 |
R₀ 的计算
R₀ 在依赖图中可以这样近似:
R₀(包) = 出度(包) × 漏洞暴露概率 × 传播转化率
- 出度:这个包被多少其他包依赖(不是它依赖谁,而是谁依赖它)
- 漏洞暴露概率:这个包的版本范围中招的概率
- 传播转化率:依赖它的包有多少会因为这个漏洞而实际被影响
高 R₀ 的包是优先治理对象:修复一个高 R₀ 的包,解除风险的效率远高于修复十个叶子包。
超级传播者识别
流行病学中著名的 20/80 法则在这里同样成立:大约 20% 的包贡献了 80% 的传播路径。
在 npm 生态中,符合”超级传播者”特征的包通常有:
- 深而宽的间接依赖:express 被 15000+ 个包间接依赖,但很少有人直接依赖它
- 版本碎片化:一个包有 200+ 个活跃版本在使用,每个版本都有自己的漏洞窗口
- 传递依赖的传递依赖:A→B→C→D,四层链条上的任何一个断点都能阻止传播,但往往没人知道自己在链条上
识别超级传播者的实际价值:用相同的修复资源,优先修超级传播者,解除风险量是修叶子包的 10-100 倍。
免疫缺口:被忽视的沉默受害者
流行病学中,”免疫缺口”是指人群中有足够多的人接种疫苗后,未接种者也得到了保护(群体免疫)。在依赖图中,”免疫”对应的是有替代路径或条件依赖。
免疫缺口指的是:
- 某包 P 没有直接依赖漏洞包 V
- 但通过路径 P→A→B→V,间接触达了漏洞
- 更糟的是:P 没有任何替代路径,也没有任何条件可以解除这个依赖
这类包是最危险的:它们既不是直接受害者(容易被发现),也不享受群体免疫(间接路径没有替代),它们是沉默的下一波受害者。
安全治理的操作化
将流行病学框架落地到实际安全运营:
操作一:R₀ 排序的 CVE 响应
不再按”直接依赖数”排序响应优先级,改用 R₀ 估计值:
响应优先级 = R₀(漏洞包) × 受影响版本使用率
这样可以把资源集中在”修复一个,解困一千”的包上。
操作二:超级传播者建档
维护一个组织内部的超级传播者清单,包含:
- 哪些内部包是高度中心化的依赖节点
- 这些包对应的外部库的 R₀ 估计值
- 这些包的可替代性评分
在采购新依赖之前,先查该库的传播影响半径,而不是只查 CVE 列表。
操作三:依赖路径实时扫描
在 CI/CD 中集成深度依赖路径分析,不只是检查直接依赖,还追踪 N 层间接依赖:
- 当新的 CVE 披露时,能立即回答”它在第几层影响了我们哪些系统”
- 当考虑引入新依赖时,能估算”这个包如果被污染,传播半径有多大”
局限性
- R₀ 估计依赖准确的全量依赖图:实际中 npm/pip 的完整依赖图是巨大的,计算 R₀ 需要对整个生态做全局分析
- 时间维度:包版本的使用率随时间变化,R(t) 也是动态的
- 条件依赖和可选依赖:Cargo.lock 里的 optional 依赖在流行病学模型里如何建模还不清晰
- 修复传播的博弈:漏洞披露后,包的维护者会主动升级,但升级行为本身也是一种”免疫响应”,模型需要包含这层反馈
核心隐喻:不要再问”log4j 影响了多少个包”。要问:”log4j 的 R₀ 是多少?它的超级传播者是谁?我们的免疫缺口在哪里?”
💬 评论
💡 使用 GitHub 账号登录 即可参与讨论