Web 安全漏洞之檔案上傳

公子發表於2019-07-01

clipboard.png

檔案上傳漏洞及危害

檔案上傳漏洞是指網路攻擊者上傳了一個可執行的檔案到伺服器上,當開發者沒有對該檔案進行合理的校驗及處理的時候,很有可能讓程式執行這個上傳檔案導致安全漏洞。大部分網站都會有檔案上傳的功能,例如頭像、圖片、視訊等,這塊的邏輯如果處理不當,很容易觸發伺服器漏洞。這種漏洞在以檔名為 URL 特徵的程式中比較多見。嗯,是的說的就是世界上最好的語言 PHP。例如使用者上傳了一個 PHP 檔案,拿到對應檔案的地址之後就可以執行它了,其中的危害自然不言而喻。那在 Node.js 中就沒有檔案上傳漏洞了麼?答案肯定是否的。除了可執行檔案外,還有以下幾個潛在的問題。
<!--more-->

檔名

使用者上傳的檔案裡有兩個東西經常會被程式使用,一個是檔案本身,還有一個就是檔名了。如果檔名被用來讀取或者儲存內容,那麼你就要小心了。攻擊者很有可能會構造一個類似 ../../../attack.jpg 的檔名,如果程式沒有注意直接使用的話很有可能就把伺服器的關鍵檔案覆蓋導致程式崩潰,甚至更有可能直接將 /etc/passwd 覆蓋寫上攻擊者指定的密碼從而攻破伺服器。

有些同學可能會說了,/ 等字元是檔名非法字元,使用者是定義不了這種名字的。你說的沒錯,但是我們要知道我們並不是直接和使用者的檔案進行互動的,而是通過 HTTP 請求拿到使用者的檔案。在 HTTP 表單上傳請求中,檔名是作為字串儲存的。只要是合法的 HTTP 請求格式,攻擊者可以構造請求中的任何內容用於提交給伺服器。

POST /upload HTTP/1.1
Host: test.com
Connection: keep-alive
Content-Length: 4237161
Accept: */*
Origin: http://test.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary9pQqgBGwpDfftP8l
Referer: http://test.com
Accept-Encoding: gzip, deflate
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8,zh-TW;q=0.7,da;q=0.6

------WebKitFormBoundary9pQqgBGwpDfftP8l
Content-Disposition: form-data; name="file"; filename="../../attack.jpg"
Content-Type: image/jpeg

------WebKitFormBoundary9pQqgBGwpDfftP8l--

HTML 和 SVG

雖然說 Node.js 在檔案上傳服務端可執行程式的漏洞沒有 PHP 那麼高,但是除了服務端可執行之外我們還有客戶端可執行問題,所以還是要做好防備。假設使用者可以上傳任意格式的檔案,而如果攻擊者上傳了 HTML 檔案後可以配合 CSRF 攻擊進一步製造 XSS 攻擊。

如果你是一個圖片上傳的介面,如果你僅限制 HTML 格式的話也存在問題,因為圖片中有一種特別的存在是 SVG 格式。SVG 是一種向量圖形格式,它使用 XML 來描述圖片,在其內部我們是可以插入 <html>, <style>, <script> 等 DOM 標籤的。如果不對 SVG 中的檔案內容進行過濾的話,也會發生意想不到的效果。

<svg viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <script>alert(111)</script>
    <rect x="25" y="25" width="50" height="50" />
</svg>

軟鏈

我們知道在作業系統中軟鏈本質上也是一種檔案,只是這個檔案中不包含實際的內容,它包含另外一個檔案的路徑名。可以是任意檔案或目錄,可以連結不同檔案系統的檔案。如果攻擊者上傳了一個軟鏈檔案,軟鏈描述對應的是 /etc/passwd 的話,攻擊者利用程式可以直接讀取到伺服器的關鍵檔案內容,導致伺服器被攻陷。

伺服器磁碟

除了檔案本身的問題之外,還有一種情況我們需要考慮到的是檔案上傳之後的處理。如果我們將使用者上傳的檔案儲存到了本地,而沒有限制使用者的上傳頻率的話,就很有可能被攻擊者利用。攻擊者會頻繁的上傳檔案導致伺服器磁碟佔用 100%,撐爆伺服器之後沒辦法處理程式的其它任務進而導致伺服器當機。

防禦方法

針對以上幾個可能出現的漏洞場景,我們需要做到以下幾點:

  1. 對使用者傳入的檔名在使用的時候儘量進行白名單過濾,可以的話儘量不要使用使用者傳入檔名,杜絕從檔名上導致的安全漏洞。
  2. 對檔案內容本身做好格式驗證,黑名單或者白名單的方式都可以,不過白名單的方式安全性會更高一點。檔案格式不能簡單的判斷檔案字尾或者是表單上傳時帶有的 Content-Type 欄位,因為這兩個是使用者上傳內容,都是可被構造的。最好是通過檔案頭的魔術數字來讀取,配合白名單列表就能避免這方面的問題。比較著名的使用魔術數字來判斷檔案型別的模組是 https://github.com/sindresorh...,推薦直接使用。
  3. 如果允許使用者上傳 .svg 格式圖片的話,需要針對 SVG 內容進行 HTML 解析,過濾掉 <script>, <foreignObject> 等相關標籤。當然,使用白名單的話是最好不過的了。這裡提供一個比較全的 SVG 合法標籤白名單列表

需要額外提醒的是,如果使用者上傳的壓縮包,程式有解壓的行為,那麼不僅要按照上述規則校驗壓縮包本身,還需要按照相同的邏輯校驗解壓之後的所有內容。同時針對伺服器磁碟被撐爆的情況,推薦限制使用者的上傳頻率降低風險,同時增加磁碟監控告警實時關注線上伺服器的狀態。如果儲存到本地不是必須的話也可以使用外部儲存服務來降低伺服器這塊的風險。

相關文章