不知不覺來到了 Input (下篇),這次要來介紹 "規範 Input 寫入的範圍":
有關規範 Input 寫入的範圍,我們需要確認兩點:
確認檔案方式:
驗證檔案格式 & 副檔名
確保根據允許的文件類型白名單檢查上傳文件的文件副檔名。在服務器端執行此操作,因為可以繞過客戶端檢查。
注意: 此處若只有驗證附檔名肯定是不夠的,若遇到有心人士,還有可能會把文件副檔名改掉,改成其他期望的副檔名,因此,我們仍然需要驗證檔案格式:
如何驗證檔案格式?
以圖片為例,我們可以使用 File Signature
來確認是否為圖片:
若要評斷 png,我們就可以判斷該 file 的前幾個 byte 是否與 png 檔的 file signature 前幾個 byte 相符 ?
以 NodeJS 為例,就可以以 Buffer 形式 get 上傳的檔案物件,並把其 Uint8Array 格式的資料取前幾個值來進行比對:
function isPngOrNot(yourArray) {
if (!yourArray || yourArray.length < 8) {
return false;
}
return yourArray[0] === 0x89
&& yourArray[1] === 0x50
&& yourArray[2] === 0x4E
&& yourArray[3] === 0x47
&& yourArray[4] === 0x0D
&& yourArray[5] === 0x0A
&& yourArray[6] === 0x1A
&& yourArray[7] === 0x0A;
}
驗證 Content-type header
從瀏覽器上傳的文件將附有 Content-Type 標頭。確保提供的類型屬於允許文件類型的白名單。
以 NodeJS 為例,我們可以以下方式查看:
app.use('/api/', (req, res, next) => {
// 使用 req.is 來判斷 content type
if (req.is('application/json')) {
// do something
}
});
注意: 有經驗的攻擊者有可能會透過寫簡單的 script 來繞過這段檢查
(https://www.hacksplaining.com/prevention/file-upload)
過濾掉可能是惡意的檔案
隔離你的上傳 (確保 Input 最終存入的位置與原本的 code 分開)
可以考慮將 code 與上傳需要存儲的文件的地點分開 (將上傳的文件存儲在遠程文件服務器或單獨的磁盤分區中,也有助於隔離惡意文件可能造成的潛在損害。)
或者,可以將上傳的文件寫入DB,避免直接去執行該文件。
確保無法執行上傳文件
無論最終存儲上傳文件的地點為何 (例如: 他是被寫入 disk),確保它們以操作系統知道且並不會將它們視為可執行代碼的方式寫入。
換句話說,Web server process 應該對用於存儲上傳內容的目錄 "具有讀寫權限,但不應該能夠執行那裡的任何文件"。
以 NodeJS 為例,我們可以使用 fs 來確認 file 是否為 executable:
const fs = require('fs');
testFile = fs.statSync('test_file');
// 使用 fs 的 StatSync,我們可以取得 testFile.mode (可以用 mode 確認他的檔案狀態)
注意: 如果您使用的是基於 Unix 的操作系統,請確保上傳的文件在文件權限中沒有 executable 標誌。
來到了第三天,發現自己還需要訓練一下組織能力以及研究的速度,下班要重訓又要發文真的直接變成時間管理大師XD