此为历史版本和 IPFS 入口查阅区,回到作品页
Ryan Vale
IPFS 指纹 这是什么

作品指纹

安全工具終於學會先說「不」

Ryan Vale
·
·
小團隊無法每次重做供應鏈威脅分析;更好的工具會讓安裝、更新與發布在沒有人明確決定時先少做一步。

晚上十一點,你只想替一個小問題發修正版。

先跑一次 npm install,讓環境跟 lockfile 對齊;再合併 Dependabot 剛開的更新;最後讓 CI 把套件送上 npm。每一步都很日常,也都被包在「自動化」這個讓人放鬆的詞裡。

可是這三個動作分別在做很不一樣的事:讓陌生套件執行程式碼、把剛發布的版本帶進專案,以及用一組憑證公開新的套件版本。順利時,它們替我們省下很多時間。出事時,風險也沿著同一條路自動往前走。

小團隊最缺的通常不是安全知識,而是能夠一直保持警覺的注意力。沒有人能在每次安裝、每張更新 PR、每次發布時,都重新做一輪供應鏈威脅分析。把責任放在警告訊息與文件裡,等於要求最疲累的那個人替系統補上最後一道判斷。

最近 npm 與 GitHub 的幾項改動,讓我看到一個比較務實的方向:當沒有人明確做決定時,工具先少做一步。

安裝依賴,不該順便取得執行權

npm install 給人的直覺是「把程式碼下載到本機」。長久以來,它還可能觸發 dependency lifecycle scripts 或隱含的 node-gyp 建置。這些機制有正當用途,但也讓安裝依賴同時成為執行程式碼的入口。

npm 12 把這個方向倒了過來。Dependency lifecycle scripts、隱含的 node-gyp builds,以及 Git 和 remote URL dependencies,都需要明確允許才會執行或解析。團隊可以用 npm approve-scripts --allow-scripts-pending 整理待核准項目,再把 allowlist 寫進 package.json。

這個指令只是一小部分。比較有用的變化,是「信任誰執行程式碼」終於能出現在版本控制裡。

以前,某位開發者在終端機按下確認,其他人未必知道他放行了什麼。現在 allowlist 的變動可以進入 PR diff。Reviewer 至少有機會問:這個套件為什麼需要 install script?它執行哪一段程式?lockfile 是否同時出現不合理的來源變化?

當然,進入 allowlist 不等於取得安全證書。今天看過的腳本,下一版仍可能改變;已被信任的套件,也可能在日後出現問題。預設不執行只是縮小第一時間的攻擊面,並把原本藏在安裝流程裡的決定拉到檯面上。

它也一定會造成摩擦。有些既有專案升級後,原本默默完成的建置可能突然停住。這不是可以忽略的遷移成本。但我寧可讓中斷發生在安裝時,留下清楚的待核准項目,也不想等到套件已經執行完,才靠事後紀錄猜它做過什麼。

三天冷卻,不是把所有更新都拖慢

供應鏈風險的另一個麻煩,是新版本最吸引人的時刻,往往也是外界最不了解它的時候。

Dependabot 現在會等一般版本在 registry 存在至少三天,再建立 version update PR。這三天不是保證,也不是說三天後套件就可信。它只是留出一段觀察時間,讓被撤回的版本、明顯異常或社群很快發現的問題,不必在發布後幾分鐘內就進入每個開啟自動更新的 repo。

這項預設有一個不能漏掉的例外:security updates 仍然會立即開 PR。已知漏洞需要修補,和一個剛出現、風險尚不明的新版本,是兩種不同情況。把它們全部延遲,或全部追求即時,都只是把判斷省略掉。

團隊仍可在 .github/dependabot.yml 調整 cooldown,甚至關掉它。真正的好處,是一般更新不再預設搶快。需要追最新版的專案可以明確選擇速度;其他團隊則先得到一個不必每天重新決定的觀察窗口。

三天並不神奇。這個預設沒有替你回答「多久最安全」,只是先替注意力有限的人選了一個比較保守的失敗方向。

CI 不需要永遠握著發布權

安裝與更新至少還停留在 repo 裡。發布權限一旦被濫用,錯誤版本會直接出現在公開 registry。

傳統做法是在 CI 存一把長效 npm token。只要 workflow 能讀到它,就可能直接 publish。這很方便,卻把「這次工作流是誰」和「它手上剛好有一把能做什麼的鑰匙」混成同一件事。Token 沒有過期、權限過大或 secret 外洩,代價都可能延續很久。

npm trusted publishing 改用 OIDC,讓每次發布取得短效、綁定特定工作流的憑證。CI 不必長期保存一把 publish token。若再使用 stage-only permission,套件可以先完成建置與暫存,最後由維護者用 2FA 核准是否公開。

這個切分很合理。通過測試表示產物符合目前的檢查,不代表它應該立刻對外發布。自動建置和公開套件本來就是兩個決定,只是過去常被一條 pipeline 黏在一起。

OIDC 也不會替你審查套件內容。若 workflow 本身被改壞、測試不足,或打包進錯誤檔案,短效憑證照樣可能發布有問題的版本。它處理的是身分與憑證暴露面,不是產品正確性。人工批准也只有在批准者看得到版本差異、測試結果與產物內容時,才不會淪為另一個按鈕。

把警覺寫進設定,而不是留在記憶裡

如果團隊沒有專職安全人員,我會先把幾個很普通的問題拿到下一次維護時間討論。

哪些 install scripts 真的必要?答案應該進 allowlist,而且每次新增都要在 PR 裡看得見。

一般版本更新願意等多久?三天可以先當起點;若某個專案確實需要即時跟進,就替它留下明確的例外,不要讓所有 repo 一起回到零等待。

CI 是否還保存能直接公開套件的長效 token?如果 trusted publishing 已經跑穩,舊 token 就不該因為「也許哪天會用到」而繼續存在。

這些答案都不必很華麗。它們只需要能被版本控制、被 review,也能在緊急時被有紀錄地覆寫。Override 仍然要留著,只是它應該從日常習慣變成一個看得見的例外。

工具先說「不」,不會讓供應鏈突然安全。維護者仍要看 diff、跑測試、理解依賴,也要為放行負責。

但至少在所有人都沒有做決定時,風險不會再悄悄從安裝一路自動走到公開發布。對一個只有幾雙眼睛的小團隊來說,這已經是很實際的進步。

Source notes

CC BY-NC-ND 4.0 授权