对象:用 Next.js(或类似的JS框架)做应用并公开上线的人。尤其面向「靠AI帮忙做出来、运维却在摸索」的人。这是从真实事故(→ 放任CVSS 10.0的经历)中提炼出的、用以不掉队的四根支柱。
本站的视角:胜负关键不在“速度”
个人开发者在安全上输掉,大多并非知识不足,而是 「运维的可持续性」。比谁都早读到CVE速报并没有价值(速度交给厂商或新闻即可)。真正有效的是 「让与自己相关的CVE不被漏看的机制」。所以本文不偏向“知识”,而偏向“机制化”。
1. 以生产实际版本判定
「是否在用危险的版本」,要用实际运行的版本来衡量,而非 package.json 的下限表记。因为清单里的数字与实际运行的版本常常对不上。
✗ 按 package.json 的下限计数
"next": "^16.0.0" →误判为「16.0.0脆弱所以危险」。被过度统计为 「8个脆弱」。
✓ 按生产实际版本计数
用 npm ls next 确认实际解析出的版本 →已自动更新的是安全的,只有固定锁定的才危险。实际上是2个。
# 确认实际解析出的版本
npm ls next react react-dom
# 在运行中的容器内确认
docker exec <container> npm ls next像 ^16.0.0 这样的脱字符表记,有些范围会在重新构建时自动升上去,而固定锁定的依赖则会被遗留下来。这里显示出的数字才是真相。
2. 对依赖做机器监控
靠人力追踪CVE是不可能的,而那种遗漏会酿成事故。让机器来盯住。
可免费起步的依赖监控
# 用 OSV 扫描器检查锁定文件(CI里加一步即可)
osv-scanner scan -L pnpm-lock.yaml若用 GitHub,就启用 Dependabot(Settings → Security)。只有与你的依赖相关的CVE会自动以PR形式通知你。
监控中优先级最为关键。按本站的看法,优先顺序由「是否真的正在被利用(KEV)」与「CVSS的高低」相乘来决定。CVSS数字再高,若未被使用则影响不大;分数中等但正在被利用,则要最优先——这种优先级判断才是运维的本体。
3. 更新纪律:不要遮掩症状,而要根治
一旦发现漏洞,就把框架本体更新到修复版,堵住漏洞本身。
遮掩症状(不充分)
- 用反向代理只挡掉「看起来像那样」的请求
- 停掉扣费或症状就当作「处置完成」
- RCE 本身仍然存活、被放任
根本修复(正确)
- 把框架本体更新到修复版以堵住漏洞
- 更新后,用日志确认漏洞的征兆是否消失
- 把止血与根本修复作为两项独立工作都做到
容易犯的错误
「停掉了扣费或症状」≠「处置完成」。止血,与堵住漏洞本身,是两项独立工作。两者都做到,才算真正完成。
4. 用最小权限缩小爆炸半径
设计成即便万一被攻破,也能把损害封住。
以非特权用户运行
USER 设为 root。即便被攻破,损害也只波及到“那个权限”为止。DB·Redis只放在内部网络
按服务做隔离
这些会成为分水岭:泄露时究竟是「止于窃取容器内的env」,还是「一路打到主机被接管」。
本站自身也在实践同样的对策
本站自身也按本文所写,把自己的依赖纳入CVE监控对象。让作为导火索的那起事故(漏看已公开的CVSS 10.0)不再被任何人漏看——把让机器来盯住做成产品的说服力。我们之所以写「按优先级(KEV+CVSS)来看」,是因为我们自己就是这样运维的。
延伸阅读
- 事故:放任已公开的RCE而被恶意刷费的经历
- 术语:CVE / CVSS / RCE
- 历史:Log4Shell — 经由依赖的RCE
FAQ
QNext.js 的安全里最重要的是什么?
在自己不写出bug之前,更重要的是『不要放任框架本体或依赖中已公开的CVE不管』。许多严重事故并非新型攻击,而是源自对已知漏洞的放任。
Q看 package.json 就能判断是否脆弱吗?
不能。package.json 里的 `^` 表记(下限)与实际情况不同。有些范围会在重新构建时自动更新,有些固定锁定的依赖则会被遗留下来。请务必以『实际运行的版本』来判定。
Q个人开发该最先做的一件事是?
启用 Dependabot(GitHub)或 osv-scanner,让机器替你盯住依赖的CVE。人力巡查必然会有遗漏。靠机制持续下去,比知识更有效。