2019 年 9 月 18 日 08:40
npm 漏洞不容小覷,開發(fā)人員都需要重視 npm 的安全性最佳實踐脂凶。為提高代碼安全性宪睹,本文列出了針對開源維護(hù)人員和開發(fā)人員的十大最佳安全實踐和生產(chǎn)力提示。
你重視 npm 漏洞嗎蚕钦?無論是前端還是后端開發(fā)人員亭病,都應(yīng)該重視 npm 的安全性最佳實踐。開源代碼的安全審查是提升安全性的關(guān)鍵所在嘶居,其中 npm 包的安全性應(yīng)該是首要考慮因素罪帖,因為我們發(fā)現(xiàn)即使是官方的 npm 命令行工具也很容易受到攻擊。
本文提供的這份備忘列表中邮屁,我們將列出針對開源維護(hù)人員和開發(fā)人員的十大最佳安全實踐和生產(chǎn)力提示整袁。我們從一個經(jīng)常遇到的典型錯誤開始吧:那就是人們會把自己的密碼添加到他們發(fā)布的npm 包里!
1. 不要把秘密發(fā)布到 npm 存儲庫上
無論你是使用 API??令牌、密碼還是其他類型的秘密芋忿,它們都很容易泄漏到源代碼控制中炸客,甚至泄露到公共 npm 存儲庫上的已發(fā)布包里「旮郑可能你的秘密就放在工作目錄里嚷量,存在.env 后綴的文件內(nèi);這些文件本應(yīng)添加到.gitignore逆趣,這樣就能防止把它們提交給 SCM蝶溶。但如果你直接從項目目錄里發(fā)布 npm 包會發(fā)生什么呢?
npm CLI 會將項目打包到 tar 存檔(tarball)宣渗,然后推送到存儲庫上抖所。具體哪些文件和目錄會添加到 tarball 取決于下列規(guī)則:
- 如果存在.gitignore 或.npmignore 文件,則在準(zhǔn)備發(fā)布包時痕囱,該文件中的內(nèi)容會被忽略田轧。
- 如果兩個 ignore 文件都存在,則所有不在.npmignore 中的內(nèi)容都將發(fā)布到存儲庫鞍恢。經(jīng)常就是這種情況把人搞糊涂了傻粘,這個問題還可能會把秘密泄露出去每窖。開發(fā)人員可能會記得更新.gitignore 文件,但忘了更新.npmignore弦悉,結(jié)果潛在的敏感文件沒有被推送到源代碼控制中窒典,但仍然包含在 npm 包里。
另一個好習(xí)慣是使用 package.json 中的 files 屬性稽莉,該屬性可作為白名單來用瀑志,指定要包含在要創(chuàng)建和安裝的包中的文件數(shù)組(而 ignore 文件則用作黑名單)。files 屬性和 ignore 文件可以并用污秆,以明確哪些文件應(yīng)該包含在包里劈猪,哪些應(yīng)該排除在外。兩者并用時良拼,前面 package.json 中的 files 屬性比 ignore 文件優(yōu)先級更高战得。
包發(fā)布時,npm CLI 將詳細(xì)顯示正在創(chuàng)建的存檔內(nèi)容庸推。為了萬無一失贡避,在發(fā)布命令中添加一個–dry-run 參數(shù),在發(fā)布到存儲庫之前先看看 tarball 里面到底放了什么東西予弧。
2019 年 1 月刮吧,npm 在他們的博客上提到他們添加了一種機制:如果他們發(fā)現(xiàn)一個包里有一個令牌,就會自動撤銷這個令牌掖蛤。
2. 強制鎖文件
我們都很歡迎包鎖文件的出現(xiàn)杀捻,它引入了:跨不同環(huán)境的確定性安裝,以及跨協(xié)作團(tuán)隊強制執(zhí)行的依賴項期望蚓庭。一切都很美好致讥!但是…如果我改動了項目的 package.json 文件但是忘了把鎖文件提交到它旁邊會怎么樣呢?
在依賴項安裝期間器赞,Yarn 和 npm 的反應(yīng)是一樣的垢袱。當(dāng)他們檢測到項目的 package.json 和鎖文件之間存在不一致時,會基于 package.json 的清單安裝與鎖文件中記錄內(nèi)容不同的版本港柜。
這種情況對于構(gòu)建和生產(chǎn)環(huán)境都可能存在威脅请契,因為它們可能會引入非預(yù)期的軟件包版本,讓鎖文件形同虛設(shè)夏醉。
所幸有一種方法可以強制要求 Yarn 和 npm 引用鎖文件爽锥,遵循鎖文件指定的一組依賴項及其版本。任何不一致都將中止安裝畔柔。命令行如下所示:
- 如果你正在使用 Yarn氯夷,請運行 yarn install --frozen-lockfile。
- 如果你正在使用 npm靶擦,運行 npm ci腮考。
3. 忽略運行腳本雇毫,最小化攻擊面
npm CLI 使用包運行腳本。如果你曾經(jīng)運行過 npm start 或 npm test踩蔚,那么你也使用了包運行腳本棚放。npm CLI 基于程序包可以聲明的腳本構(gòu)建,并允許包來定義在安裝到項目期間在特定入口點運行的腳本寂纪。例如席吴,有些腳本 hook 入口可能是 postinstall 腳本赌结,正在安裝的包將按照這個腳本的順序執(zhí)行清理工作捞蛋。
惡意用戶可以使用這個功能在安裝軟件包后運行任意命令來創(chuàng)建或更改軟件包,以執(zhí)行惡意行為柬姚。我們已經(jīng)發(fā)現(xiàn)的一些案例包括著名的拟杉,收集 npm 令牌的 eslint-scope 事件,此外還發(fā)現(xiàn)了 36 個對 npm 存儲庫發(fā)起域名搶注攻擊的軟件包量承。
你可以應(yīng)用下面這些 npm 安全最佳實踐搬设,以最大程度地減少惡意模塊攻擊面:
始終審核你安裝的第三方模塊并做盡職調(diào)查,以確認(rèn)其健康度和可信度撕捍。
- 不要盲目升級到新版本拿穴;嘗試新的軟件包版本之前先等其他人用一段時間來觀察。
- 在升級之前忧风,請務(wù)必查看升級版本的更改日志和發(fā)行說明默色。
- 安裝軟件包時,請確保添加–ignore-scripts 后綴以禁止第三方軟件包執(zhí)行任何腳本狮腿。
- 考慮將 ignore-scripts 添加到.npmrc 項目文件或全局 npm 配置中腿宰。
4. 評估 npm 項目的健康狀況
過時的依賴項
盲目地將依賴項持續(xù)升級到最新版本并不是什么好主意,每次升級前都應(yīng)該仔細(xì)查看發(fā)行說明缘厢、代碼更改吃度,并且通常應(yīng)該全面測試過新版后再去升級。話雖如此贴硫,坐視依賴項版本逐漸過時而根本不去升級椿每,或者經(jīng)過很長時間才升級一次,也可能會帶來不少麻煩英遭。
npm CLI 可以向你提供有關(guān)你使用的依賴項的新鮮度信息拖刃,以及它們的語義版本控制偏移量。你可以運行 npm outdated 這條命令來查看哪些包已經(jīng)過期了:
黃色的依賴項對應(yīng)于 package.json 清單中指定的語義版本控制,而紅色的依賴項表示有可用的更新税灌。此外均函,這個輸出還顯示每個依賴項的最新版本亿虽。
呼叫大夫
你安裝了各種各樣的 Node.js 包管理器,可能還在路徑中安裝了許多不同版本的 Node.js苞也,那么該如何驗證健康的 npm 安裝和工作環(huán)境呢洛勉?無論你是在開發(fā)環(huán)境還是在 CI 中使用 npm CLI,都必須評估一切是否按預(yù)期工作如迟。
何不找來一位大夫呢收毫!npm CLI 帶有一個健康評估工具,可以用來診斷你的環(huán)境殷勘,確保其具備良好的 npm 交互此再。你可以運行 npm doctor 來檢查你的 npm 設(shè)置:
- 檢查官方 npm 存儲庫是否可訪問,并顯示當(dāng)前配置的存儲庫玲销。
- 檢查 Git 是否可用输拇。
- 查看已安裝的 npm 和 Node.js 版本。
- 檢查各種文件夾(例如本地和全局 node_modules贤斜,以及用于包緩存的文件夾)的權(quán)限策吠。
- 檢查本地 npm 模塊緩存以校驗正確性。
5. 審核開源依賴項中的漏洞
npm 生態(tài)系統(tǒng)是所有語言生態(tài)系統(tǒng)中最大的應(yīng)用程序單一存儲庫瘩绒。這個存儲庫和其中的無數(shù)庫是 JavaScript 開發(fā)人員的工作重心所在猴抹,讓他們能夠利用其他人構(gòu)建好的工作并將其合并到自己的代碼庫中。話雖如此锁荔,在應(yīng)用程序中越來越多地采用開源庫也會增加引入安全漏洞的風(fēng)險蟀给。
許多流行的 npm 軟件包都被發(fā)現(xiàn)了漏洞,所以如果沒有對項目依賴項進(jìn)行適當(dāng)?shù)陌踩珜徍硕檎剑赡軙o項目帶來很大的風(fēng)險坤溃。典型案例包括 npm request 、 superagent 嘱丢、 mongoose 薪介,甚至是安全相關(guān)的包,如 jsonwebtoken 和 npm validator 等越驻。
僅僅在安裝軟件包時掃描安全漏洞是不夠的汁政,還應(yīng)該簡化開發(fā)人員工作流程,以便在軟件開發(fā)的整個生命周期中持續(xù)保證安全性缀旁,并在部署代碼后持續(xù)監(jiān)控记劈。
掃描漏洞
下面的 npm 安全最佳實踐是使用 Snyk 掃描安全漏洞,使用以下命令:
復(fù)制代碼
$ npm install -g snyk$ snyk test
當(dāng)你運行 Snyk 測試時并巍,Snyk 會報告它找到的漏洞并顯示易受攻擊的路徑目木,以便你跟蹤依賴關(guān)系樹以搞明白是哪個模塊引入了漏洞。最重要的是懊渡,Snyk 為你提供可操作的補救建議刽射,你可以使用 Snyk 在存儲庫中打開的自動拉取請求升級到修復(fù)版本军拟,或者在沒有可用修復(fù)的情況下應(yīng)用 Snyk 提供的補丁來緩解漏洞。Snyk 為易受攻擊的軟件包推薦可用的最小 semver 升級誓禁,這是一個智能升級功能懈息。
監(jiān)控開源庫中發(fā)現(xiàn)的漏洞
安全工作并沒有到此結(jié)束。
在部署應(yīng)用程序后摹恰,應(yīng)用程序的依賴項中又發(fā)現(xiàn)新的安全漏洞該怎么辦辫继?這就是為什么我們要把安全監(jiān)控與項目開發(fā)的生命周期緊密集成在一起的原因所在。
我們建議將 Snyk 與你的源代碼管理(SCM)系統(tǒng)(如 GitHub 或 GitLab)集成在一起俗慈,讓 Snyk 主動監(jiān)控你的項目并:
- 自動打開 PR 以升級或修補易受攻擊的依賴項姑宽。
- 掃描并檢測拉取請求,避免引入開源庫中的漏洞姜盈。
如果你無法將 Snyk 集成到 SCM 中低千,則可以通過以下這條簡單的命令監(jiān)控從 Snyk CLI 工具發(fā)送的項目快照:
復(fù)制代碼
$ snyk monitor
Snyk 與 npm 審核有什么不同配阵?
- 我們推薦大家閱讀由 Nearform 發(fā)布的博文馏颂,其中比較了 npm 審核與 Snyk 之間的差異。
- Snyk 的漏洞數(shù)據(jù)庫通過其威脅情報系統(tǒng)提供有關(guān)漏洞的全面數(shù)據(jù)棋傍,提供更好的覆蓋范圍救拉,并能夠顯示和報告尚未收到 CVE 的漏洞。例如瘫拣,npm 提示的漏洞中有 72%是先被添加到 Snyk 數(shù)據(jù)庫里的亿絮。在此處查看更多信息。
6. 使用本地 npm 代理
npm 存儲庫是最大的包集合麸拄,可供所有 JavaScript 開發(fā)人員使用派昧,也是 Web 開發(fā)人員的大多數(shù)開源項目的主頁所在地。但有時你可能在安全性拢切、部署或性能方面有不同的需求蒂萎。在這種情況下,npm 允許你切換到另一個存儲庫:
當(dāng)你運行 npm install 時淮椰,它會自動與主存儲庫通信以解析所有依賴項五慈;如果你想使用另一個存儲庫那也非常簡單:
- 設(shè)置 npm set registry 以設(shè)置默認(rèn)存儲庫。
- 對單個存儲庫使用參數(shù)–registry主穗。
Verdaccio 是一個簡單的輕量級零配置私有存儲庫泻拦,只需輸入下面的命令就能安裝它了:
復(fù)制代碼
$ npm install --global verdaccio
輕而易舉就能托管你自己的存儲庫争拐!下面來看看這個工具最重要的一些功能:
- 它支持 npm 存儲庫格式,包括私有包功能晦雨、作用域支持架曹、包訪問權(quán)限控制和 Web 界面中的用戶身份驗證灯抛。
- 它提供了掛鉤遠(yuǎn)程存儲庫的功能,以及將每個依賴項路由到不同存儲庫和緩存 tarball 的能力音瓷。你應(yīng)該代理所有依賴項來減少重復(fù)下載并節(jié)省本地開發(fā)和 CI 服務(wù)器的帶寬对嚼。
- 作為身份驗證提供程序,它默認(rèn)使用 htpasswd 安全選項绳慎,但也支持 Gitlab纵竖、Bitbucket 和 LDAP。你也可以使用自己的選項杏愤。
- 可以很容易地使用其他存儲提供程序來擴展靡砌。
- 如果你的項目基于 Docker,使用官方鏡像是最佳選擇珊楼。
- 它為測試環(huán)境提供了非惩ㄑ辏快的引導(dǎo),并且可以方便地測試大型單一存儲庫項目厕宗。
它運行起來相當(dāng)簡單:
復(fù)制代碼
$ verdaccio --config /path/config --listen 5000
如果你用 verdaccio 作為本地私有存儲庫画舌,可以考慮配置你的包強制發(fā)布到本地存儲庫,避免開發(fā)人員意外發(fā)布到公共存儲庫上已慢。要做到這一點曲聂,請將以下內(nèi)容添加到 package.json:
復(fù)制代碼
“publishConfig”: {“registry”: "https://localhost:5000"}
你的存儲庫已經(jīng)準(zhǔn)備就緒了!現(xiàn)在要發(fā)布一個包佑惠,只需使用 npm 命令 npm publish朋腋,它就準(zhǔn)備好與世界分享了。
7. 負(fù)責(zé)任地披露安全漏洞
當(dāng)發(fā)現(xiàn)新的安全漏洞時膜楷,如果在沒有事先警告的情況下公開披露它們旭咽,或者沒能為用戶提供適當(dāng)?shù)木徑獯胧﹣肀Wo(hù)他們,那么這種行為反而會帶來潛在的嚴(yán)重威脅赌厅。
我們建議安全研究人員遵循負(fù)責(zé)任的披露計劃穷绵,計劃應(yīng)該將研究人員與漏洞資產(chǎn)的供應(yīng)商或維護(hù)者聯(lián)系起來,提供相應(yīng)的流程和指南來傳達(dá)漏洞信息察蹲、告知關(guān)聯(lián)方漏洞的影響和能力请垛。在對漏洞正確分類后,供應(yīng)商和研究人員應(yīng)該協(xié)調(diào)制作漏洞的修復(fù)程序并商討發(fā)布日期洽议,以便在公開安全問題之前為受影響的用戶提供升級路徑或補救措施宗收。
安全太重要了,不能亡羊補牢亚兄,也不能隨意處置混稽。我們 Synk 非常重視安全社區(qū),并認(rèn)為負(fù)責(zé)任地披露開源軟件包中的安全漏洞可以幫助我們確保用戶的安全和隱私。
Snyk 的安全研究團(tuán)隊定期與社區(qū)合作獎勵漏洞的報告匈勋,例如在數(shù)百個社區(qū)中披露的 f2e-server 案例等礼旅;Snyk 還與弗吉尼亞理工大學(xué)等學(xué)術(shù)研究方密切合作,以提供專業(yè)的安全知識和能力洽洁,與供應(yīng)商和社區(qū)維護(hù)者協(xié)作痘系。
我們邀請你與我們合作,并在披露過程中提供幫助:
- 在這個網(wǎng)址負(fù)責(zé)任地披露安全漏洞饿自,或者向我們發(fā)送郵件(security@snyk.io)汰翠。
- 我們的披露政策可以參閱這里。
8. 啟用 2FA
2017 年 10 月 npm 正式宣布昭雌,支持使用 npm 存儲庫托管其封閉和開源軟件包的開發(fā)人員進(jìn)行雙重身份驗證(2FA)复唤。
盡管現(xiàn)在 npm 存儲庫已經(jīng)支持了 2FA,但似乎它的推廣還是比較緩慢烛卧;一個例子是在 2018 年中期 ESLint 團(tuán)隊的開發(fā)者帳戶被攻擊者盜取佛纫,導(dǎo)致惡意版本的 eslint 傳播,亦即 eslint-scope 事件总放。
緊急安全警告請分享呈宇。
今天,我們發(fā)現(xiàn) eslint-scope 的版本 3.7.2 中包含了竊取你的 NPM 憑據(jù)的惡意代碼间聊。如果你使用的是 3.7.2 版攒盈,請立即采取措施抵拘。
Snyk 數(shù)據(jù)庫條目
——Snyk(@snyksec)哎榴,2018 年 7 月 12 日
對于 npm 最佳安全實踐而言,啟用 2FA 是一項簡單但意義重大的步驟僵蛛。存儲庫支持兩種模式來在用戶帳戶中啟用 2FA:
- 僅限授權(quán)——用戶通過網(wǎng)站或 CLI 登錄 npm尚蝌,或執(zhí)行其他操作(如更改配置文件信息)時。
- 授權(quán)和寫入模式——配置文件和登錄操作充尉,以及諸如管理令牌和包的寫操作飘言,以及對團(tuán)隊和包可見信息的改動時。
首先為自己準(zhǔn)備一個身份驗證應(yīng)用程序(例如谷歌身份驗證就可以驼侠,它能安裝在移動設(shè)備上)姿鸿,然后就可以開始了。一種簡單方法是通過 npm 的用戶界面來輕松啟用 2FA 擴展防護(hù)倒源。如果你喜歡用命令行苛预,則可以使用支持的 npm 客戶端版本(>=5.5.1)時輸入以下命令輕松啟用 2FA:
$ npm profile enable-2fa auth-and-writes
按照命令行提示啟用 2FA,并保存緊急身份驗證代碼笋熬。如果你希望僅為登錄和配置文件更改啟用 2FA 模式热某,則可以在上面的代碼中使用 auth-only 替換 auth-and-writes。
9. 使用 npm 作者令牌
每次你使用 npm CLI 登錄時,系統(tǒng)都會為你的用戶賬戶生成一個令牌昔馋,并對你的 npm 存儲庫進(jìn)行身份驗證筹吐。使用令牌可以在 CI 和自動化過程中輕松執(zhí)行與存儲庫相關(guān)的 npm 操作,例如訪問存儲庫上的私有模塊或從某個構(gòu)建步驟中發(fā)布新版本秘遏。
可以通過 npm 存儲庫網(wǎng)站管理令牌丘薛,也可以使用 npm 命令行客戶端。使用 CLI 創(chuàng)建限制為特定 IPv4 地址范圍的只讀令牌邦危,示例如下:
$ npm token create --read-only --cidr=192.0.2.0/24
要查看你的用戶賬戶創(chuàng)建了哪些令牌榔袋,或在緊急情況下撤消令牌,你可以分別使用 npm token list 和 npm token revoke 命令铡俐。
請保護(hù)好自己的令牌凰兑,盡量避免傳播給別人,以遵循這條 npm 最佳安全實踐审丘。
10. 了解模塊命名約定和域名仿冒攻擊
命名模塊可能是創(chuàng)建包時要做的第一件事吏够;取名前要注意,npm 定義了包名必須遵循的一些規(guī)則:
- 它限制為 214 個字符滩报。
- 它不能以點或下劃線開頭锅知。
- 名稱中沒有大寫字母。
- 沒有尾隨空格脓钾。
- 只有小寫售睹。
- 不允許使用某些特殊字符:“~\’!()*”)’。
- 不能以 . 或 _ 開頭可训。
- 不能使用 node_modules 或 favicon.ico昌妹。
即使你遵循了這些規(guī)則,還要注意 npm 在發(fā)布新包時使用了垃圾郵件檢測機制握截,會給包名打分飞崖,并判斷包名是否違反服務(wù)條款。如果違反了條件谨胞,存儲庫可能會拒絕該請求固歪。
Typosquatting 是一種攻擊方式,它利用了用戶所犯的錯誤(例如拼寫錯誤)胯努。攻擊者可以通過仿冒域名將惡意模塊發(fā)布到 npm 存儲庫上牢裳,給模塊取一個和現(xiàn)有流行模塊非常相似的名稱。
我們一直在跟蹤 npm 生態(tài)系統(tǒng)中的一些惡意軟件包叶沛;它們也出現(xiàn)在 PyPi Python 存儲庫中蒲讯。最典型的例子包括 cross-env 、 event-stream 和 eslint-scope 恬汁,它們都曾成為惡意攻擊的目標(biāo)伶椿。
域名仿冒攻擊的一項主要目標(biāo)就是盜取用戶憑據(jù)脊另,因為任何包都可以通過全局變量 process.env 訪問環(huán)境變量导狡。我們見過的案例中,event-stream 的情況是攻擊者針對開發(fā)人員偎痛,希望將惡意代碼注入應(yīng)用程序的源代碼旱捧。
文章最后再來看看下面這些提示,減少這類攻擊的風(fēng)險:
- 將軟件包安裝說明復(fù)制粘貼到終端時要格外小心踩麦。確保在源代碼存儲庫以及 npm 存儲庫中驗證這確實是你要安裝的軟件包枚赡。你可以使用 npm info 驗證包的元數(shù)據(jù),以獲取有關(guān)貢獻(xiàn)者和最新版本的更多信息谓谦。
- 在你的日常工作流程中記得不用時隨時登出 npm 用戶賬戶贫橙,這樣你的憑據(jù)就不會成為別人入侵你帳戶的缺口。
- 安裝軟件包時反粥,請附加–ignore-scripts 以降低任意命令執(zhí)行的風(fēng)險卢肃。例如:npm install my-malicious-package --ignore-scripts。
全文內(nèi)容可在此下載 pdf 版本才顿。
一定要打印出這份備忘清單并將其固定在某處莫湘,隨時提醒自己;如果你是一名 Javascript 開發(fā)人員郑气,或者只是喜歡使用 npm幅垮,你都應(yīng)該遵循這些 npm 最佳安全實踐。