依赖不是别人的代码,是你发布的代码里的代码。
一、凌晨的一条高危告警
2026 年 3 月 14 日,凌晨两点十七分,许砚的手机响了。依赖机器人报告一个间接依赖存在反序列化漏洞,等级 high。syncwell 的发布包里包含这个依赖,影响所有 1.8.2 及以下版本。
以下情境为教学假设,人物、引语和数字不代表经核实的真实案例。
他在四十分钟内做完了三件事:确认调用路径是否可达、跑一遍回归、判断是否需要立即发版。结论是可达但需要非默认配置,风险中等。36 小时后,1.8.3 发布,安全公告同步贴出。这是这一年里的第一次公告,也是他第一次意识到:开源项目的安全成本不在写代码,在「随时可能被打断」。
被打断的成本不只是那 40 分钟。发版之后的三天,他要盯着 issue 区看有没有人报告升级问题;这三天里几乎写不了新代码。安全事件真正吃掉的,是事件之后的一段注意力。
全年下来,依赖扫描产生 214 条告警:高危 11 条、中危 63 条、低危 140 条。平均修复时间 4.2 天,其中高危 2.0 天、中危 4.2 天、低危 4.4 天。安全与依赖维护总共花了 74 小时,平均每条告警 20.7 分钟。
214 条告警不是 214 个漏洞。 其中大量是同一个库的不同版本重复告警,还有一些在调用路径上根本不可达。把它们全部当成必须修,会让维护者陷入「永远在升级」的状态;把它们全部忽略,则会在某一天收到一封来自下游的问责邮件。
还有一种情况是「已修复但未关闭」。依赖升级之后,扫描器仍会报告,因为锁定文件里还留着旧版本;这类告警占了 214 条里的 41 条,处置动作只是更新锁定文件,平均 3 分钟。
下游问责的措辞通常很客气,但要求很具体:影响哪些版本、什么时候修复、有没有临时缓解措施。这三个问题如果没有现成答案,沟通会拖上一周;而一周之后,事情往往已经在别的地方被公开。
二、214 条告警的分布与分流
告警处理的第一步不是修,是分流。分流的依据只有两条:调用路径是否可达、修复是否会引入破坏性变更。两条都答完,一条告警才有明确的处置。
| 等级 | 条数 | 占比 | 平均修复 | 处置原则 |
|---|---|---|---|---|
| 高危 | 11 | 5.1% | 2.0 天 | 可达即发版,不可达也发版 |
| 中危 | 63 | 29.4% | 4.2 天 | 可达排进下个版本,不可达记录 |
| 低危 | 140 | 65.4% | 4.4 天 | 合并到季度升级窗口 |
| 合计 | 214 | 100.0% | 4.21 天 | — |
高危的处置原则最严格:只要在发布包内,无论调用路径是否可达,都按发版处理。原因是「不可达」这个判断本身会出错,而错误的代价是不对称的——误判一次不可达,可能是一次真实的数据泄露;多发一次版,只是多花两小时。
不可达的判断要留痕。他会写清三件事:调用路径、为什么不可达、如果判断错了最坏会怎样。第三件事最有用,它把「我觉得没事」变成一条可以被下一个人复核的记录。
中危按可达性分流。63 条里有 28 条可达,进入下个版本;35 条不可达,记录在案并在季度窗口顺带升级。低危不单独发版,统一进季度升级窗口;140 条里大部分是同一个库的传递依赖,一次升级能解决十几条。
四个季度窗口分别处理了 31、28、36、45 条告警。窗口期固定为每季度第二周的周六上午,两小时;这个时间选择的原因不是效率,是可预期:用户知道这个项目每季度会有一个维护版本,不会在周五晚上收到破坏性更新。
季度窗口还有一个附带好处:它给用户一个明确的升级时间表。运维可以在日历上标出四个周六,提前安排测试;比起「随时可能发版」,可预期本身就是一种服务质量。
分流的标准要写下来,不能每次重新判断。 写下来的好处是:半年后你已经忘了当时为什么跳过某条告警,但文档还在;贡献者也能照着同一套标准处理,不会因为「维护者不在」而停摆。
标准要能被新人执行。他的写法是三条判定句:「在发布包内 + 路径可达 = 发版」「在发布包内 + 路径不可达 = 记风险、下个版本发」「不在发布包内 = 季度窗口」。三句话不需要上下文,任何人读完都能做同样的决定。
三、三次安全公告与 72 小时响应
这一年发了三次安全公告,每一次都对应一个具体的触发点与一条可复用的模板。
| 公告日期 | 触发 | 影响版本 | 从确认到修复 |
|---|---|---|---|
| 2026-03-14 | 依赖反序列化漏洞 | ≤ 1.8.2 | 36 小时 |
| 2026-07-02 | 自研路径穿越 | ≤ 1.9.0 | 52 小时 |
| 2026-09-18 | 配置项凭据泄露 | 全部 | 18 小时 |
三次公告的平均修复时间是 35.3 小时,全部在 72 小时内完成。这个数字不是能力,是流程:确认 → 评估影响 → 修复 → 发版 → 公告,五步各有一个人能独立完成的动作,不需要等任何人。
公告模板只有五段:影响版本、问题描述、用户该做什么、修复版本、时间线。五段都不超过五行,因为安全公告的读者是正在决定「要不要现在升级」的人,不是来读故事的。
公告里最容易漏的是「用户该做什么」。写「请升级到 1.8.3」不够,还要写「升级前请备份配置」「不需要轮换凭据」「旧版本将在 30 天后停止支持」;三句具体动作,比一段解释更有用。
第三次公告最值得说。它不是代码漏洞,是文档里的一个配置示例把占位符写成了真实格式的密钥,用户复制后可能没有替换。修复方式是改文档、加一条启动检查、在公告里明确「不需要轮换,但请检查配置」。这类问题不在扫描器覆盖范围内,只能靠发布前的文档审阅发现。
文档审阅可以做成一个两分钟的检查:搜一遍所有示例里的密钥格式、内网地址与真实域名。三项都替换成占位符,再检查占位符是否会被误认为可直接使用。这个检查已经写进发版清单的第 4 项。
72 小时不是承诺给用户的,是承诺给自己的。 没有这个时限,一个高危告警可以在待办里躺两周,而维护者会觉得「反正没人知道」。公开一个时限,等于给自己装了一个闹钟。
私密报告渠道也要有。SECURITY.md 里写清一个邮箱、一个 72 小时初判承诺、一条「请勿公开 issue」的说明。这一年通过私密渠道收到 4 份报告,其中 2 份有效,1 份是误报,1 份是上游问题。四份都在 48 小时内回复了。
私密渠道的关键是「回复得快,而不是修得快」。报告者最怕的是石沉大海;一封「已收到,正在确认,48 小时内给结论」的邮件,能把大部分焦虑消掉。修复可以慢,确认不能慢。
四、供应链与一次复算
依赖安全的上游是供应链。一个人维护的项目没有资源做完整的供应链加固,但有三件事成本低、收益明确:锁定文件进版本库、发布产物带校验、构建可复现。
锁定文件保证「今天装的和昨天装的是同一套依赖」;没有它,npm install 每次都可能引入一个新版本。发布产物带校验,让用户能验证下载的文件没有被替换。构建可复现是更高要求,做不到全部,至少做到「同一台机器、同一份锁定文件,两次构建产出一致」。
可复现构建的第一步是去掉构建时间戳与随机顺序。这两项通常藏在打包工具里,改起来不难;改完之后,两次构建的哈希才会一致,校验值才有意义。
这三件事不能防住所有攻击,但能防住最常见的一类:依赖被投毒后,锁定文件让升级变成一次显式动作,而不是自动发生。
锁定文件也有代价:它会让安全更新变成手动动作。折中的做法是「锁定 + 每周自动开一个升级 PR」,让机器人做机械部分,人只做判断。
复算:
告警:11 + 63 + 140 = 214
占比:11/214 = 5.1%;63/214 = 29.4%;140/214 = 65.4%
加权修复:(11×2.0 + 63×4.2 + 140×4.4) ÷ 214
= (22 + 264.6 + 616) ÷ 214 = 902.6 ÷ 214 = 4.22 天 ≈ 4.2
安全工时:74 h;74 ÷ 214 = 0.346 h = 20.7 分钟/条
告警频率:365 ÷ 214 = 1.7 天/条
季度窗口:31 + 28 + 36 + 45 = 140 条(低危全部 + 部分中危)
公告修复:(36 + 52 + 18) ÷ 3 = 106 ÷ 3 = 35.3 小时
私密报告:4 份;有效 2 份 → 50.0%
复算里最容易被忽略的是 140 这个数字出现了两次:一次是低危告警数,一次是四个季度窗口处理的告警数。它们相等是巧合,不是设计;如果你的低危告警更多,窗口处理的条数会超过低危数,因为中危也会顺带升级。
还有一个数字值得记:214 条告警里有 88 条来自三个包。把这三个包的升级单独排期,能消掉四成告警;这比逐条处理高效得多。
五、行动清单与复算验收
- 打开依赖扫描,统计过去 12 个月的告警总数与等级分布。
- 对每条高危告警确认调用路径是否可达,把判断写进记录。
- 写下分流标准:可达、不可达、需发版、可合并,四类各一句话。
- 固定四个季度升级窗口,写进 README 的维护节奏。
- 建
SECURITY.md,写清私密报告渠道与 72 小时初判承诺。 - 准备一份五段式安全公告模板,放在仓库里随时可用。
- 确认锁定文件进版本库,发布产物带校验值。
- 每年演练一次:从收到报告到发布公告,走完整流程并计时。
验收标准:任取一条高危告警,都能说出它是否可达、几天内修、要不要发公告。
前四条是「把告警变成队列」,后四条是「把响应变成流程」。如果只有一小时,先做第三条:没有分流标准,214 条告警会变成 214 次重新决策。
清单里最容易被跳过的是第八条。演练不是为了发现问题,是为了让流程在没有压力的时候跑一遍;真出事时,你需要的是一份跑过的流程,不是一份写得很好的文档。
使用边界与实操验收
214 条告警不等于 214 个漏洞。 重复、不可达、已修复未关闭都会计入;分流之后真正需要动作的通常不到三分之一。
4.2 天是均值,不是承诺。 高危的 2.0 天才是需要守住的线;均值会被大量低危拉长,不能拿来对用户承诺。
72 小时是初判,不是修复。 初判包括确认收到、评估影响、给出初步时间;修复可能更久。把两者混为一谈,会让承诺变成空话。
供应链加固没有终点。 锁定文件、校验值、可复现构建是三件优先级最高的事,做完这三件再考虑更复杂的方案;一个人没有资源把每一层都做到满分。
不要隐瞒已经披露的漏洞。 公告写清影响范围与时间线,比写得漂亮更重要;用户需要的是判断依据,不是措辞。
关键参考与核验范围
- CISA Secure by Design :用于核对安全责任前移与披露实践的组织方式。
- SLSA 供应链 levels :用于核对构建与产物的可验证性分级。
- OWASP Top Ten :用于核对常见风险类别,不用于推导本文的告警数量。
以上链接于 2026-10-26 实测可公开访问;未核验版本、功能范围与政策更新。核验日期:2026-10-26。
范围上只讨论一个人维护的开源项目如何做依赖分流、安全公告与响应时限,不讨论渗透测试、漏洞赏金与合规认证。
系列导航
- 上一篇:Issue 分类:10.7 分钟一个、72 小时首响与 134 个遗留
- 下一篇:赞助之门:4,200 个 star 里的 23 个人与两家公司
- 回到目录:一个人的开源产品:4,200 个 star、一百八十个付费与一年 612 小时的维护
- 相关阅读:一个人的安全基线:十四个入口、十七个密钥与一次 72 小时响应
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。