對象:用 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。人力巡查必然會有遺漏。靠機制持續下去,比知識更有效。