NodeJS路徑遍歷:示例及預防

Jaxu發表於2024-05-08

  讓我們來看看什麼是路徑遍歷攻擊,以及在Node.js中可以採用哪些方法來阻止這種攻擊。

  構建一個安全而健壯的應用程式需要考慮的因素很多,並非一件容易的事情。要確保覆蓋所有潛在的漏洞是一項十分艱鉅的任務,這需要大量的經驗和指導。在這些漏洞中,有一個和系統目錄訪問安全相關的漏洞,它通常會在程式進行路徑遍歷時受到攻擊。

  理解這一點有助於你找到解決問題的辦法。網路上有大量相關的資源可以幫助你解決該問題,本文只是諸多資源中的一個。

  本文旨在介紹路徑遍歷攻擊的原理,以及使用Node.js可以採取哪些方法來阻止這類攻擊。首先,我們將簡要介紹什麼是路徑遍歷攻擊,然後將探討一些常見的示例,最後介紹如何修復這些漏洞。透過本文的介紹,你應該對路徑訪問遍歷有一個基本的瞭解,並能夠在你的程式中阻止相關的漏洞。

  請注意,本文是專門針對Node.js開發人員的。因此,我們希望你對Node.js開發棧有一個基本的瞭解。如果你還沒有嘗試過,請檢視Node.js指南以獲得更多的資訊。

路徑遍歷介紹

  什麼是路徑遍歷攻擊?它是一種利用伺服器端糟糕的訪問控制漏洞而實現的攻擊,特別是對檔案的訪問。在這類攻擊中,攻擊者試圖透過嚮應用程式中注入無效或惡意的內容來訪問伺服器上受限制的檔案。可以把它看作是SQL隱碼攻擊,但針對的是系統目錄而不是資料庫。

  很明顯,能夠訪問伺服器上未經授權的檔案,其後果可想而知。攻擊者一旦獲取了這種許可權,他就可以在我們的系統中造成非常嚴重的破壞,從而危及使用者資訊。

  下面讓我們更加深入地研究導致此漏洞的原因,以及你的伺服器執行什麼樣的系統是何等重要。現在,讓我們先看看幾個路徑遍歷攻擊的示例。

路徑遍歷攻擊示例

  你可能會問,一個典型的路徑遍歷攻擊是什麼樣的?

  其實很簡單:

../../etc/passwd

  出乎你的預料,對嗎?

  路徑遍歷攻擊的核心思想就是找到一種方法,訪問開發人員和應用程式不希望你訪問的資料夾。因此,如果你對如何訪問路徑和Linux命令列有所瞭解的話,你就可以在不受保護的應用程式中進行相應的操作,從而實現路徑遍歷攻擊。

  下面是一些具體的例子。

相對路徑攻擊

  相對路徑攻擊本質上就和我們上面所描述的一樣。如果應用程式缺少有效的使用者輸入驗證,攻擊者可以利用該漏洞試圖訪問伺服器中受限制的檔案。這樣情況下,passwd檔案中包含我們在伺服器上的金鑰就有可能被洩露。

  當然,你可以增強使用者輸入驗證,以減輕這種漏洞所帶來的風險。一種簡單的方法是使用path.normalize()並對使用者輸入進行特殊字元過濾——順便說一下,你應該始終對使用者輸入的內容進行過濾和驗證——這可以避免許多問題。

有毒的Null位元組攻擊

  在HTTP請求中,透過在字串的末尾新增NULL位元組和\0,攻擊者可以繞開使用者輸入驗證和過濾,從而訪問未經授權的檔案和目錄。

  那會是怎樣的呢?就像這樣:

/../../../../../../../../../../../../../../../../etc/passwd%00

  請注意末尾的%00。我們的驗證程式最終可能會將其翻譯為\0.txt\0,從而允許訪問passwd檔案。

  為了阻止此類攻擊,你只需要新增下面的程式碼對使用者輸入的內容進行驗證:

if (user_input.indexOf('\0') !== -1) {
return respond('Access denied');
}

  還算簡單,對吧?

  路徑遍歷攻擊其實並不複雜。正如我們前面所提到的,它依賴於糟糕的訪問控制邏輯的實現或者一些邊界值判斷。不過這些漏洞可能非常危險,我們應該儘可能地阻止或減少這些漏洞的發生,好訊息是要做到這一點並不難。

其它阻止Node.js中路徑遍歷攻擊的方法

  當然,還有許多方法可以用來增強我們應用程式的安全性。JavaScript已經足夠成熟,它提供了大量的文件用以指導我們採用不同的方法來減少攻擊,這裡我將列出其中一部分。

路徑字首驗證

  在應用程式中只允許某種級別的遍歷會怎麼樣?在某些情況下,你可能希望應用程式允許使用者在不同的資料夾中查詢檔案——例如,個人資料圖片和文章都存放在不同的資料夾中。你可以在程式碼中進行硬編碼來驗證路徑字串,就像請求特定資源時使用的變數一樣,但是這樣做可能導致字首路徑遍歷攻擊。

  如果使用者被允許在應用程式中輸入點和斜線等字元,那麼攻擊者就可以自由地遍歷目錄。為了阻止這種情況的發生,我們需要驗證使用者的輸入並過濾掉這些字元,或者直接顯示錯誤資訊。

白名單

  白名單是一種直接有效的方法,它可以減少漏洞被利用的可能性。當然,你不能指望白名單一定能派上用場,但在需要的時候總是能起到一定的作用。

  一個簡單的例子是驗證使用者輸入的內容是否符合某個預定義的標準。例如,如果你規定應用程式只允許建立和處理路徑中包含小寫字母和數字字元的檔案,那麼你就可以驗證使用者輸入的內容中只包含這類字元。

if (!/^[a-z0-9]+$/.test(user_input)) {
return respond('Access denied');
}

  透過新增以上驗證方法,你可以針對惡意攻擊新增一層額外的防護。

路徑連線

  最後,我們將實現一個通用的驗證方案,為我們可能面臨的所有這些漏洞提供一個較為健壯的方法,最終生成一個安全的路徑字串。

  這個解決方案的程式碼看起來是這樣的:

var root = '/var/www/';
exports.validatePath = (user_input) => {
if (user_input.indexOf('\0') !== -1) {
return 'Access denied';
}
if (!/^[a-z0-9]+$/.test(user_input)) {
return 'Access denied';
}
var path = require('path');
var safe_input = path.normalize(user_input).replace(/^(\.\.(\/|\\|$))+/, '');
var path_string = path.join(root, safe_input);
if (path_string.indexOf(root) !== 0) {
return 'Access denied';
}
return path_string;
}

  正如你所看到的,我們將前面討論過的所有檢查和驗證方法合併到一起,包括可能對系統造成的任何風險。

寫在最後

  JavaScript是一種成熟而健壯的語言,因此有無數種方法可以解決這個問題。然而這些方法都不是最完美的,只有滿足你的需求並進行了充分測試的方法才是最好的。

  雖然解決方法看起來很簡單,但要確保執行良好的路徑遍歷安全策略十分重要。我們需要儘可能地覆蓋應用程式中的潛在缺口。當然,技術在不斷更新,將來會出現更強大、更全面的解決方案來防止此類問題的發生。只要我們的方法有效且具備創造性,我們總會堵住系統中存在的漏洞。

原文地址:Node.js Path Traversal Guide: Examples and Prevention

相關文章