- 大多數 npm 問題源自於環境配置問題,例如執行策略和權限,而不是 npm 本身的問題。
- 使用確定性安裝
npm ci並謹慎使用npm audit降低供應鏈和脆弱性風險。 - 避免
sudo npm減少不必要的依賴項,並使用使用者級前綴,使全域安裝更安全、更穩定。 - 詳細的日誌記錄、npm doctor 和偶爾的徹底重新安裝是診斷和解決頑固的 npm 錯誤的重要工具。
遇到奇怪的 npm 問題可能會非常令人沮喪,尤其是當你只想安裝一個套件然後繼續編碼的時候。 從 Windows 上的 PowerShell 阻止腳本,到 Linux 上的權限噩夢,再到審計報告中無窮無盡的漏洞列表,如果您不知道自己在看什麼,npm 錯誤會迅速演變成數小時的生產力損失。
本指南將帶您了解使用 npm 時最常見的實際問題,解釋問題發生的原因,並為您提供經過實戰檢驗的實用解決方案。 我們將探討 Windows 執行策略、全域權限錯誤、npm 生態系統中的安全陷阱、開發環境和生產環境漏洞之間的區別,以及其他相關內容。 npm ci 確實如此,以及如何在不驚慌的情況下調試損壞的安裝和快取問題。
PowerShell 執行原則阻止 Windows 上的 npm。
許多 Windows 使用者在安裝 Node.js 後遇到的第一個障礙是 npm 根本無法在 PowerShell 中運作。 終端拋出類似「無法載入檔案」的錯誤訊息 C:\Program Files\nodejs\npm.ps1 因為此系統上已禁用腳本運行”,以及 PSSecurityException 並建議閱讀 about_Execution_Policies.
這個問題與 Node.js 安裝錯誤無關;它是 PowerShell 的安全功能,稱為執行策略。 預設情況下,某些 Windows 設定會阻止任何本機腳本(包括 npm 自帶的 PowerShell 封裝器)執行,這使得 PowerShell 無法正常運作。 npm.ps1 可能包含不安全內容。
要解決此問題,通常需要放寬目前使用者的 PowerShell 執行策略,而不是完全停用系統層級的安全性。 常見的方法是以管理員身分執行 PowerShell,並使用類似這樣的命令: Set-ExecutionPolicy RemoteSigned -Scope CurrentUser允許本地建立的腳本運行,同時阻止未簽署的遠端腳本運行。
如果您完全不想更改 PowerShell 策略,可以使用命令提示字元 (cmd.exe) 或具有不同 shell 的 Windows 終端來解決這個問題。 在這些環境中,npm 不會透過 PowerShell 腳本運行,因此該限制不適用,您的 npm 只要 Node.js 已正確加入到 PATH 環境變數中,指令就應該可以運行。
npm CI 的真正作用以及它為何重要
npm 運行後,另一個經常引發疑問的命令是 npm ci它的行為方式與我們更熟悉的有所不同。 npm install. 雖然兩者都會安裝依賴項, npm ci 專為乾淨、可重現的環境而設計,例如持續整合 (CI) 管道。
關鍵的區別在於 npm ci 忽略版本範圍 package.json 並完全安裝指定版本。 package-lock.json. 這意味著不會因為發佈時間較晚,就將「相容但更新的」依賴版本偷偷塞進你的建置中;只要 lockfile 保持不變,每次安裝都是確定性的。
從性能角度來看, npm ci 對於持續整合 (CI) 來說,速度通常更快,因為它跳過了某些依賴項解析步驟,並假定一切都是全新的。 它期望你的 node_modules 目錄要么為空,要么將被清除,這使得 npm 可以避免很多額外的檢查和更新。 npm install 通常會執行。
從安全性和供應鏈的角度來看, npm ci 大幅降低未經審核的依賴項變更混入生產版本中的風險。 由於它從不查找更新的兼容版本,因此實際上將依賴關係樹凍結到您的團隊已鎖定和審核的內容,從而使事件重現和漏洞分析變得更加容易。
以安全為中心的團隊通常會結合 npm ci 使用自動化依賴項掃描工具檢查每個軟體包,包括那些被鎖定的軟體包。 package-lock.json 文件。 這樣,即使你的鎖定檔案在提交時是乾淨的,新發現的漏洞或惡意軟體包仍然可以在應用程式部署之前在 CI 建置期間被捕獲。
全域 npm 權限和「永遠不要使用 sudo npm」規則
在類別 Unix 系統(Linux、macOS)上,npm 問題中最臭名昭著的一類問題就是使用提升的權限安裝全域套件。 如果您曾經看到過類似「缺少寫入權限」的警告, /usr/lib/node_modules或類似這樣的錯誤 EACCES: permission denied您遇到了這類問題。
預設情況下,npm 通常會嘗試將全域安裝的套件放在下列位置: /usr (例如 /usr/lib/node_modules 以及可執行檔 /usr/bin),這些是通常由 root 使用者擁有的系統目錄。 當用戶開始運行 sudo npm install -g ... 為了「修復」權限錯誤,檔案和目錄的擁有者變成了 root 用戶,導致之後以普通用戶身份運行的命令遇到寫入存取問題。
最重要的一點很簡單:不要以 root 使用者身分執行 npm,並避免使用 sudo 除非你對自己在做什麼有十足的把握,否則不要使用 npm。 除了權限混亂之外,以 root 使用者身分安裝第三方 JavaScript 還會增加任何惡意或被入侵軟體包的影響,使其完全控制您的系統。
要查看 npm 目前將全域套件放置在哪裡,您可以運行 npm config get prefix通常會回傳類似這樣的內容 /usr 在一個有問題的設定上。 這個前綴決定了全域模組及其二進位檔案的最終位置,因此如果該前綴指向系統路徑,從長遠來看,權限問題幾乎不可避免。
一個安全且建議的解決方案是將全域 npm 前綴移至使用者的主目錄中,這樣您就可以在無需提升權限的情況下擁有完全控制權。 典型的做法是建立一個目錄,例如: ~/.npm-global 然後運行 npm config set prefix '~/.npm-global' 這樣,未來所有全球安裝都會安裝在那裡,而不是在 /usr.
更改前綴後,必須將新的全域二進位檔案目錄新增至 PATH 環境變數中,以便系統可以找到全域安裝的命令。 例如,您可以新增一行類似這樣的程式碼: export PATH=~/.npm-global/bin:$PATH 新增到你的 shell 啟動檔案(例如) ~/.bashrc or ~/.zshrc然後重啟終端,使變更生效。
配置正確後,重新運行 npm doctor 這會成為一個不錯的健全性檢查:它應該報告快取檔案和全局 node_modules 當前用戶可以讀取和寫入這些文件。 請注意,當您切換到新的全域目錄時,先前安裝的全域軟體包將不再存在,您需要重新安裝您實際使用的軟體包。
使用 npm doctor 診斷環境問題
許多 npm 問題並非由特定項目引起,而是由機器上損壞或不一致的 npm 環境引起。 命令 npm doctor 正是為此而生的:它會對您的 npm 設定進行一系列健康檢查,並突出顯示潛在的問題。
當你執行 npm doctornpm 會測試與登錄機碼的連線性,驗證您的 npm 和 Node.js 版本,檢查您設定的登錄 URL,並檢查快取資料夾和全域模組目錄的權限。 每次檢查都會報告“正常”或“異常”狀態,這很容易發現配置錯誤。
例如,如果 npm 發現諸如以下目錄: /usr/lib/node_modules or /root/.npm 如果普通使用者無法寫入,您將看到與權限相關的項目被標記為紅色「notKk」。 這強烈暗示 npm 之前是以 root 使用者身分執行的,或透過其他方式運行的。 sudo留下 root 使用者擁有的文件,阻礙正常操作。
doctor 命令還可以揭示 npm 所期望的缺失工具,例如 Git,某些依賴項使用 Git URL 而不是已發布的註冊表包,因此需要 Git。 如果 Git 沒有安裝或不在您的 PATH 環境變數中,您將看到一條警告,提示您安裝 Git 並重試。
在解決所有問題後 npm doctor 再次執行報表應該會顯示所有綠色「ok」狀態,表示 npm 安裝正常。 每當您懷疑系統範圍內的 npm 配置可能是導致安裝或審核期間出現奇怪錯誤的原因時,請將此命令視為基本健康檢查。
npm 生態系統有多脆弱:著名事件和風險
除了本地配置問題之外,還必須了解 npm 作為一個生態系統本身也存在結構性風險,這是由於龐大的依賴關係樹和主要由志願者維護造成的。 現代 JavaScript 專案通常會引入數百甚至數千個軟體包,其中許多軟體包僅由一兩個人利用業餘時間維護。
這種極度分散化使得手動審查最終申請資料中的所有內容幾乎不可能,這為以下情況的發生打開了方便之門: npm 的供應鏈攻擊 以及一些不易察覺的漏洞。 一個被破壞或棄用的軟體包可能會沿著依賴關係圖蔓延,影響大量項目,而開發人員卻不會立即意識到這一點。
這種脆弱性的一個經典例子是2016年發生的一起涉及一個叫做“ left-pad其中大約包含 11 行程式碼。 它的唯一目的是在字串左側填充一個字符,直到達到給定的長度,但它卻被無數的軟體包和主要工具(如 Babel JavaScript 編譯器)直接或間接地使用。
在作者與 npm 發生糾紛後,維護者決定取消發布他的幾個軟體包,其中包括 left-pad,來自註冊表。 由於 npm 當時沒有保留已發布版本的不可變快照,因此刪除操作立即破壞了全世界所有依賴這些確切版本的構建,導致開發人員只能面對失敗的安裝。
npm 公司採取了一項史無前例的舉措,恢復了最後一個已知的版本。 left-pad 他們未經作者同意,自行恢復了生態系統。 該決定引發了爭議,因為它與作者控制其軟體包生命週期的理念相悖,但也凸顯了關鍵基礎設施對微不足道的第三方模組的依賴程度之高。
除了可用性事件之外,還有許多以安全為中心的案例,其中流行的 npm 套件遭到破壞或被發現包含嚴重漏洞。 這些情況包括維護者受到社會工程攻擊、被遺棄的軟體包的所有權被劫持,或利用細微的漏洞執行任意程式碼。
一個被廣泛討論的例子是2018年 event-stream 攻擊者入侵了一款流行的串流媒體工具,並註入了旨在從受影響的應用程式竊取加密貨幣的程式碼。 因為 event-stream 是許多其他軟體包的依賴項,惡意程式碼透過依賴鏈悄無聲息地傳播到生產系統中。
另一個例子是 2019 年的命令注入漏洞。 coa,一個被各種知名工具使用的 CLI 助手。 在某些情況下,未經適當清理的使用者輸入可能會轉換為任意 shell 命令,如果漏洞在易受攻擊的環境中被觸發,則可能會打開遠端執行的大門。
像高知名度圖書館這樣的 axios 也存在一些漏洞,例如伺服器端請求偽造 (SSRF) 問題,攻擊者可以利用該問題重定向伺服器,向內部資源發出請求。 即使是像這樣的極其常見的實用程序 minimist 受到原型污染漏洞的影響,攻擊者可以篡改物件原型,並可能以微妙而危險的方式改變應用程式的行為。
最主要的教訓是,即使是非常流行或看似無害的軟體包也並非自動安全;它們可能像任何其他軟體一樣被利用、被棄用或被錯誤配置。 因此,維護 npm 的健康安全態勢既需要技術工具(審計、掃描、鎖定),也需要文化習慣(定期更新、謹慎選擇依賴項,以及在可行的情況下優先在內部編寫簡單的實用程式)。
開發環境與生產環境的漏洞
當開發人員首次運行 npm audit 在一個專案中,長長的漏洞清單看起來可能很可怕,但並非所有漏洞都會真正影響正在運行的生產應用程式。 許多已標記的問題都存在於僅在開發或建置時使用的工具中。
關鍵區別在於聲明的依賴項。 dependencies 以及那些 devDependencies in package.json. 包裹 devDependencies 通常只在打包、轉譯、程式碼檢查或執行測試伺服器等任務中需要,它們不應該作為最終生產包或伺服器運行時的一部分發布。
例如,工具中的漏洞,如 webpack-dev-server, @angular-devkit, 或者 vite 通常情況下,這在本地開發時很重要,但在生產版本部署後就無關緊要了。 這些開發伺服器和建置工具可能會暴露攻擊面,例如跨網域程式碼外洩或類似 SSRF 的行為,但前提是開發伺服器處於活躍運作狀態且可存取。
運行一條平原 npm audit 報告通常會包含運行時漏洞和僅開發漏洞,顯示諸如以下軟體包中的問題: brace-expansion, esbuild以及 webpack-dev-server. 審計通常建議 npm audit fix 甚至 npm audit fix --force 需要提升版本號,有時需要對 Angular 等框架進行重大更新才能消除警告。
要查看哪些漏洞實際上會影響部署到生產環境的內容,您可以執行以下命令: npm audit --production (或使用推薦的) --omit=dev 在較新版本的 npm 中可以選擇)。 如果此命令傳回“發現 0 個漏洞”,則表示根據 npm 的安全建議資料庫,您的生產相依性集目前不存在已知問題。
但這並不意味著你可以永遠忽略僅存在於開發環境中的漏洞,因為它們仍然可能在開發人員進行專案開發時危及他們的機器或原始程式碼。 但是,了解這種差異可以讓你確定優先順序:先解決影響較大的生產問題,然後有計劃地解決開發環境問題,而不是對每個警告都做出反應,就好像它們同樣重要一樣。
npm audit fix 的工作原理以及何時應避免使用 --force 參數
命令 npm audit fix 它旨在自動升級安全版本範圍內的易受攻擊的依賴項,但它並不是一個萬能的按鈕,可以毫無顧慮地解決所有問題。 它會遍歷你的依賴關係樹,尋找存在已知問題的軟體包,並嘗試將它們升級到已修復的版本,以保持與現有軟體包的兼容性。 package.json 限制。
例如,如果將依賴項指定為 ^1.2.0npm 將嘗試遷移到最新版本 1.x 包含此修復的版本,而不是直接跳到 2.x這可能會帶來重大變化。 這使得 npm audit fix 對於許多專案來說相對安全,因為它符合語義版本控制約束。
不過,有時唯一可用的補丁只存在於較新的主要版本或需要更廣泛升級的工具鏈中,這時 npm 建議使用 npm audit fix --force. 此標誌告訴 npm 允許安裝可能造成破壞性更新的更新,包括框架或建置工具的主要版本升級和級聯變更。
盲目奔跑 --force 在大型或遺留專案中,這很容易導致建置失敗或造成細微的執行階段迴歸,因為程式碼所依賴的依賴項可能會改變行為或 API。 可以把它看作是對你的技術堆疊進行一次小規模遷移,而不僅僅是打一個安全補丁,因此應該在測試和版本控制安全措施到位的情況下進行。
也有一些情況下,npm 無法自動修復所有漏洞,通常是因為必要的版本升級會與依賴關係圖中的其他約束衝突。 在這種情況下,您可能需要手動更新或替換某些庫,或接受暫時的風險,直到發布不破壞性的補丁。
一個切實可行的策略是先了解哪些漏洞會影響生產環境,然後再應用 npm audit fix 無 --force只有在進行影響分析並進行適當的測試覆蓋後,才考慮強制升級或重大升級。 這樣既能確保應用程式的安全,又不會為了追求完美無瑕的審計報告而不斷破壞程式碼庫的穩定性。
歸根結底,處理 npm 漏洞是一個持續的風險評估、優先排序和受控更新的過程,而不是執行一次命令就忘記的事情。 每個問題都需要根據其嚴重性、在您的實際環境中的可用性以及升級受影響的軟體包或工具鏈的成本來權衡。
重新思考你真正需要多少 npm 依賴項
使用 npm 最有效的長期安全實踐之一就是盡可能少地依賴第三方軟體套件。 每增加一個依賴項,就會增加攻擊面、維護負擔,以及將來可能出現的意想不到的傳遞性問題。
為了方便起見,開發者經常安裝軟體包,即使該功能可以用幾行純 JavaScript 程式碼實現。 隨著時間的推移,這種習慣會使你的依賴樹變得臃腫,其中包含許多幾乎不使用、維護不善或容易被內部程式碼片段替換的模組。
減少依賴項除了安全性之外還有多種好處:更小的專案、更快的安裝和建置時間、更少的版本衝突,以及在出現問題時更簡單的偵錯。 更精簡的依賴關係圖也更容易審核應用程式實際包含的內容,而無需瀏覽你從未主動選擇的大量臨時軟體包。
從風險角度來看,活動部件越少,意味著被遺棄的項目、受脅迫的維護人員或晦澀實用程式中的細微漏洞影響您的技術堆疊的機會就越少。 即使你無法避免大型框架或核心庫,你仍然可以選擇性地使用執行瑣碎任務的小型輔助工具,這些工具通常會造成相當一部分審計噪音。
成熟的依賴策略包括嚴格評估新軟體包,定期刪除未使用的軟體包,並儘可能優先選擇維護良好、經過廣泛審查的庫,而不是小眾或一次性的解決方案。 結合合理運用 npm audit, npm ci這種心態,加上定期更新,可以顯著降低您遇到的與 npm 相關的問題的頻率和嚴重程度。
調試 npm 錯誤、日誌和損壞的安裝
即使擁有配置良好的環境和精簡的依賴樹,你最終也會遇到令人困惑的 npm 錯誤,這些錯誤會徹底停止你的工作流程。 有效的調試始於獲取有關 npm 在命令失敗時底層實際執行的操作的更多資訊。
一個簡單的技巧是使用類似這樣的標誌來增加 npm 的詳細程度。 --dd --loglevel verbose),它會列印出該過程的詳細步驟。 這種層級的日誌記錄可以準確地揭示哪個操作失敗了,哪個檔案或目錄導致了問題,或者依賴鏈中的哪個腳本出現了故障。
每當命令執行失敗時,npm 通常也會告訴你它將更詳細的日誌檔案儲存在什麼位置,通常位於類似這樣的目錄下。 ~/.npm/_logs. 開啟該日誌可以按時間順序追蹤安裝或腳本運行,包括堆疊追蹤、環境詳細資訊和底層系統錯誤,這些錯誤並不總是出現在簡短的錯誤輸出中。
有些失敗源自於自身的錯誤。 package.json例如無效的 JSON、不正確的腳本名稱或格式錯誤的版本範圍。 在這種情況下,仔細重新檢查文件是否存在語法錯誤、拼寫錯誤或末尾逗號,可以解決乍看之下似乎很神秘的問題。
有時,根本原因在於作業系統或工具層級:網路存取問題、DNS 解析問題、防火牆規則問題,或 Git 或 GitHub 憑證配置錯誤。 例如,如果直接從 Git 倉庫拉取依賴項,而 Git 缺失或配置錯誤,即使註冊表本身可訪問,npm 也會失敗。
依賴項安裝問題也可能源自於損壞的檔案。 node_modules 目錄或 npm 緩存,尤其是在安裝中斷或升級未完成的情況下。 如果你懷疑有腐敗行為,通常更容易將其清除。 node_modules 與其嘗試修復單一損壞的軟體包,不如清除 lockfile 中的 npm 快取並重新安裝。
常見的恢復模式是刪除 node_modules(可選)運行快取清理命令,然後執行 npm install 再次從頭開始重建依賴關係樹。 這種強硬的重置方法經常可以清除常規故障排除無法發現的奇怪或不一致的行為,尤其是在切換分支或合併大型依賴項變更之後。
請記住,並非所有錯誤都是由 npm 本身直接引起的;有些錯誤源於軟體包在安裝期間運行的腳本,或者源於您自己的專案的生命週期鉤子。 詳細的日誌和錯誤堆疊追蹤可以幫助您確定您遇到的是純粹的 npm 問題,還是透過 npm 觸發的第三方腳本或自訂工具中的問題。
總的來說,結合更好的日誌記錄、仔細閱讀錯誤訊息以及偶爾的重置 node_modules 它將幫助你從大多數 npm 故障中恢復過來,而不會陷入無休止的試錯循環。 隨著時間的推移,你會發現一些反覆出現的模式——JSON 拼字錯誤、權限問題、缺少工具——這些模式會讓下一次偵錯過程快得多。
成功管理 npm 最終在於了解本地工具的特性和更廣泛的生態系統風險:從 PowerShell 執行策略和 Unix 權限,到確定性安裝和漏洞審計,再到謹慎的依賴項選擇和系統化的調試,你採用的每一個良好實踐都會降低 npm 問題導致你的開發工作失敗的可能性。
