对象:人手不多、同时运维多个 Web 应用(框架混用),想把依赖库的漏洞(CVE)处置「认真彻底地修一次」的人。这里不讲攻击手法,只讲修好、让它不会消失、并持续监控为止的实务。容易成为导火索的事故示例,可参考 放任已公开的RCE导致被恶意刷费的经历。
本站的视角:越小的团队越能靠『两个机制』守住
人手少的团队,真正管用的不是花哨的工具,而是两条纪律。①自动的变化检测(只对新增的漏洞报警)②local→push→deploy(生产端只接收,不编辑)。本站也是同样的思路,把依赖审计(pnpm audit)放在每次部署前+每日 cron 里跑,向生产端则只用 push-to-deploy 来分发。不要把它当成一次性的修复事件,而要倒向放好一个会报警的机制并持续运转的运维方式,这反而是最便宜、最持久的。
先把完成的「4个定义」固定下来
这是为了不让人随便说「搞定了」的刹车。先约定好:在这四点齐备之前都算未完成。
① 扫描
用数字掌握现状
② 修复
根治非dev的严重/高危
③ 隔离/移交
修不了·孤立的要明示
④ 监控
加上每日变化检测
① 扫描:把现状变成「数字」
首先,用机器掌握「哪里有什么」。手动的不定期检查一定会中断。
自动探索锁文件并扫描
composer.lock / package-lock.json / pnpm-lock.yaml,每天用 OSS 扫描器(osv-scanner 或 pnpm audit)跑一遍。只是读锁文件,负载极轻。要知道严重度不是『一个数字』就能定
噪声不是『无视』而是『有依据地排除』
② 修复:不要对症疗法,要「根治」
止血(对症疗法)和根治是两件不同的工作。两者都做了才算结束。
只做对症疗法(止于围堵)
- 用反向代理只挡掉「看起来像那样」的请求
- 因为症状停了就以为「处置完成」
- 脆弱的依赖原封不动=RCE等仍然存活
根治(正确)
- 把脆弱的依赖更新到修复版,堵住窟窿本身
- 更新后,用日志确认征兆是否消失
- 把止血和根治当作两件事都做
跨大版本要『在推上生产前先构建验证』
和打补丁不同,跨大版本(框架14→15、UI工具4→6 等)会伴随破坏性变更。盲目升级而导致生产构建失败是最糟的。把改完的源码放到与生产相同的运行时(同样 Node/PHP 版本的容器)里,等构建/类型检查完全通过后再 push。失败的报错要一个一个解决(同步→异步化的 codemod、CSS 的多行 class、类型命名空间被废弃 等)。若采用旧容器仍在运行的结构,失败时也能不停机地回切。
把修复的「持久性」也包含进来才算完成
内容哪怕100分,只要下一次部署就被覆盖,也是0分。这是最容易踩的坑。
不要在生产的工作树里直接commit
务必统一为 local→push→deploy 的单向流动
不要把 HTTP 200 当成『正常』
curl | grep)、错误日志里是否出现新条目、连依赖 DB 和带参数的动态路由都要走一遍。由于缓存可能让生效变慢,要隔一段时间或清缓存后再看。③ 隔离/移交:把修不了的东西「明示」出来
不一定所有问题都能马上修好。诀窍在于:对修不了的、归属他人管辖的、已经不用的,不要含糊其辞。
孤立·EOL 代码要『在删除前先隔离』
先确定确实无人引用,再动手
修不了/他人管辖的要明示移交
④ 监控:加上每日「变化检测」才算完成
走到这一步,最后放上会报警的机制。没有它,就算辛苦修好了,也察觉不到复发。
不是『把全部每天都报』而是『增加了才报』
把扫描结果与上次取差分(state file)比较,只在出现新增的严重/高危时才通知。每天都收到相同内容,很快就会被无视(告警疲劳)。通知汇总成一封邮件(新增·已解决·现状),用 cron 每日跑。哪怕是共享服务器,靠一个扫描器二进制+cron 也足够运转(负载是数毫秒量级·搭配 nice 就更稳妥)。
盘点时会一并冒出来的「秘密」和「密钥」
在彻底修复 CVE 的过程中,常常会连依赖以外的窟窿一起被发现。最有代表性的有两个——遗忘在公开目录里的秘密文件(webroot 里放着旧 token,若源自共用模板就所有机器都有同一个窟窿)和交给可能被入侵的临时环境的 root 密钥。两者都是会造成「一处泄露就全盘皆失」的典型,所以要在和 CVE 处置相同的盘点时机一并检查(这两个各自都值得单独深挖)。秘密的基础可参考 秘密的安全保管 和 最低限度检查清单。
接下来读
FAQ
Q漏洞处置做到什么程度才算『完成』?
四点齐备才算完成。①用扫描把现状变成数字②修复了非dev的严重/高危③对修不了的、归属他人管辖的、已孤立的代码做了明示的隔离/移交④加上了每日变化检测(能捕捉复发与新增的监控)。尤其在加上④之前都不算完成,因为依赖明天又可能变得脆弱。
Q每天扫描不会被告警搞得疲惫吗?
『把全部内容每天都报一遍』很快就会被无视。要与上次结果取差分,只在出现新增的严重/高危时才通知,这就是『变化检测』。不每天发送相同内容,反而是最能长期坚持下去的设计。
Q明明修好了,为什么又退回到脆弱状态?
在生产的工作树里直接commit,会在下一次部署(pull 或 checkout -f)时被覆盖,修复就消失了。务必统一为 local→push→deploy 的单向流动,让生产端『只接收』。修复内容哪怕完美无缺,只要会被覆盖的运维方式,成果就是零。