文件上傳漏洞防範-文件類型檢測

文件上傳漏洞防範-文件類型檢測

      當時需要開發一個功能,管理員可以上傳一個包含不良詞語的文本文件。系統利用這些詞語實時檢查用戶提交的內容。上傳的文件需要遵循特定的格式。

爲了防止用戶上傳文本文件以外的文件,我們可以在前端進行操作。

<input type="file" accept="text/plain" />

     這樣,用戶只需在文件選擇窗口中選擇一個文本文件即可。然而,爲了確保系統安全,僅僅在界面上阻止用戶是不夠的。有必要在後臺重新驗證上傳的文件,看看用戶是否上傳了文本文件。我們需要解決的問題是確定用戶上傳文件的實際類型。

開始

說明上述問題,我們將建立一個演示系統,前端使用 React.js,後端使用 Java/Spring Boot。

image

我們的界面非常簡單,由一個輸入[type=file]和一個上傳所選文件的按鈕組成。選擇文件時,用戶界面將顯示瀏覽器確定的 MIME 類型。上傳文件後,系統將返回後臺確定的 MIME 類型。所有源代碼都在這裏。此外,還要準備一些文件,以測試系統判斷是否正確。

image

準備 3 個擴展名正確的文件,然後複製這些文件並重命名:

real.png -> fake.txt

real.jpg -> fake.zip

real.svg -> fake.docx

後臺文件類型確定

項目的後臺系統使用 Spring Boot 以 Java 編寫。還實現了一個控制器,用於接收用戶的上傳請求。

@Slf4j
@RestController
public class UploadController {

  @PostMapping(path = "/check-file-type", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
   public ResponseEntity < Response > checkFileType(@RequestPart MultipartFile file) {
     // to be implemented
   }
}

以及將結果返回給用戶的響應

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Response {
   private int status;
   private String message;
   private String mimeType;

  public Response(String mimeType) {
     this.status = HttpStatus.OK.value();
     this.message = "Successful";
     this.mimeType = mimeType;
   }
}

使用用戶代理定義的 MIME 類型

當從 input[type=file] 中選擇文件時,文件類型已由瀏覽器(用戶代理)根據 MIME 類型格式確定,然後通過 Content-Type 請求頭傳輸到後端。因此,控制器參數中的 MultipartFile 類已經包含了文件類型的信息。

現在,您可以使用 getContentType() 根據 MIME 類型確定文件類型。

@PostMapping(path = "/check-file-type", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity < Response > checkFileType(@RequestPart MultipartFile file) {
   String mimeType = file.getContentType();
   return ResponseEntity.ok(new Response(mimeType));
}

讓我們測試一下上面準備的文件

image

     就文件 real.png 而言,用戶代理通過 .png 擴展名識別出了正確的 MIME 類型。但對於 fake.zip 文件,用戶代理無法正確識別其文件類型爲 JPG,而是通過 .zip 擴展名來確定。因此,當用戶有意更改文件名和擴展名時,依賴客戶端定義的 MIME 類型可能會有一些風險。每種文件類型都有不同的規範,存儲方式也不同,因此如果要確定文件的確切類型,就需要讀取該文件的內容。

     MIME 類型和確定文件類型的一些方法 MIME 類型(多用途 Internet 郵件擴展)是一種定義文檔、文件或字節集的性質和格式的標準。它在 IETF 的 RFC 6838 中進行了定義和標準化。MIME 類型的結構包括類型和子類型:

類型/子類型 示例:text/plain、application/zip、...

詳細說明

      類型是數據類型所屬的一般類別,如視頻或文本。 子類型決定所分類的確切數據類型。例如對於文本類型,我們可以有 plain(純文本)、html(HTML 源代碼)或 calendar(iCalendar .ics 格式)等子類型。一般來說,MIME 類型是分配給文件類型的名稱,用於確定傳輸數據的內容類型,以及基於該類型的應用程序的相應行爲。根據 MIME 類型,我們可以確定文件類型,那麼如何從一個文件中識別其 MIME 類型呢?要確定 MIME 類型,我們需要讀取其內容。每種文件類型都會有不同的存儲方式,比如 ZIP 文件的文件規範就像這裏一樣。但仍有一些共同特徵可用於識別。 文件簽名是存儲在文件開頭的模式字節(也稱爲神奇數字或神奇字節),用於識別文件的內容和格式。下表列出了一些常用格式的文件簽名(在此查看一些文件簽名)。

image

除了使用文件簽名外,有時還需要讀取文件內容來找到確切的文件類型。例如,SVG 格式本質上是 XML。因此,要確定它,除了需要讀取魔法數字來確定 XML 格式外,還需要讀取裏面更多的內容,才能正確確定 SVG 格式。

其他一些格式,如 Apple iWork,實際上是 Zip 文件中 XML 文件的集合。此時,Zip 文件負責製作包含 XML 文件的容器。由於需要解壓其中的內容,文件類型識別變得更加困難。

使用 Apache Tika

     確定 MIME 類型 在 Java 系統中,可以使用 Apache Tika 提取信息並確定文件數據的確切格式。Apache Tika 可根據以下幾個標準確定文件的數據格式:

魔數(Magic number)

文件名擴展名(File name extension):部分基於文件擴展名

從互聯網下載文件的元數據(Metadata)

定義容器及其內容(container)

要在 Maven 項目中使用 Tika,可以在 pom.xml 中添加依賴關係:

<dependency>
     <groupId>org.apache.tika</groupId>
     <artifactId>tika-core</artifactId>
     <version>2.1.0</version>
</dependency>

因此,我們可以編寫更多的函數,在文件上傳到系統時確定文件的正確 MIME 類型。

public class FileUtils {
   public static String getRealMimeType(MultipartFile file) {
     AutoDetectParser parser = new AutoDetectParser();
     Detector detector = parser.getDetector();
     try {
       Metadata metadata = new Metadata();
       TikaInputStream stream = TikaInputStream.get(file.getInputStream());
       MediaType mediaType = detector.detect(stream, metadata);
       return mediaType.toString();
     } catch (IOException e) {
       return MimeTypes.OCTET_STREAM;
     }
   }
}

在後臺再創建一個 API,並使用 Tika 來識別 MIME 類型。

@PostMapping(path = "/check-real-type", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity < Response > checkRealType(@RequestPart MultipartFile file) {
   String mimeType = FileUtils.getRealMimeType(file);
   return ResponseEntity.ok(new Response(mimeType));
}

然後,編輯用戶界面,使用新創建的 API 將文件上傳到後臺,並用一些文件再次進行測試:

image

    在文件 real.png 中,Tika 識別出了正確的 MIME 類型。在 fake.zip 文件中,儘管改名爲 fake.zip,Tika 仍能正確識別出文件的原始 MIME 類型爲 image/jpeg。

Tika 支持的格式列表請點擊此處


.netCore可以使用

https://www.nuget.org/packages/Mime-Detective
https://github.com/KevM/tikaondotnet
https://github.com/nissl-lab/toxy

Golang 可以使用

https://github.com/gabriel-vasile/mimetype
https://github.com/golang/go/blob/master/src/mime/type.go


小結

     後端系統在接收上傳文件時應驗證上傳文件的類型。根據瀏覽器檢測到的 MIME 類型來檢查文件類型可能還不夠,因爲在某些情況下,文件會被改成擴展名,以便對系統進行釣魚。每種文件類型都有不同的結構。藉助 Java 系統上的 Apache Tika,可以根據文件格式確定文件的確切類型。

總結安全相關關注點

用戶和文件系統權限

  1. 只允許授權用戶上傳文件
  2. 只允許系統用戶讀取文件(當文件公開時不適用) ,考慮使用 JWT 來確保應用程序的安全。

上傳和下載限制

  1. 設置文件大小限制
  2. 設置適當的請求限制

只要在應用程序屬性文件中添加這兩個屬性,就能輕鬆實現這一目標:

spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB

擴展和類型驗證

  1. 允許擴展的白名單。只允許安全和重要的擴展,並確保應用輸入驗證。
  2. 這樣,用戶在選擇上傳圖片時就會受到限制。

    # Example for Bootstrap-Vue
    <
    b-form-file ...
    :accept="
    .pdf,.png
    "
    />

  3. 在後臺也要驗證文件類型!不要相信 Content-Type 標頭,而是從文件名中提取擴展名(使用適當的 regex 或庫,如 Apache Commons IO),或者考慮使用 MIME-Type 檢測。然後,將其與有效類型進行比較,如果無效則顯示錯誤。
# Using Apache Commons IO. 
Returns "pdf"
import org.apache.commons.io.FilenameUtils;
String extension = FilenameUtils.getExtension("test.pdf");

     3.設置正確的 Content-Type 類型,從文件中提取,而不是使用 Content-Type 標頭。考慮使用 Apache Tika。

import java.io.ByteArrayInputStream;
import java.io.IOException;
import org.apache.tika.config.TikaConfig;
import org.apache.tika.detect.Detector;
import org.apache.tika.io.TikaInputStream;
import org.apache.tika.metadata.Metadata;public static String getContentType(byte[] fileBytes, String filenameWithExtension) throws IOException {
TikaConfig config = TikaConfig.getDefaultConfig();
Detector detector = config.getDetector();
TikaInputStream stream = TikaInputStream.get(new ByteArrayInputStream(fileBytes));
Metadata metadata = new Metadata();
metadata.add(Metadata.RESOURCE_NAME_KEY, filenameWithExtension);
return detector.detect(stream, metadata).toString();
}
文件名消毒 
  1. 將文件名改爲由應用程序生成的文件名
  2. 設置文件名長度限制
  3. 儘可能限制允許的字符數
# Always has length 36 and is limited to 23 charsimport java.util.UUID;
public static String createUniqueFilename(String extension) {
return UUID.randomUUID().toString() + "." extension;
}
文件內容驗證 
  1. 掃描文件以查找病毒。檢查 Java 病毒檢測服務或 Java Dockerized 病毒檢測服務
  2. 應用圖像重寫技術,摧毀圖像中注入的任何惡意內容。詳情請查看
  3. 轉換爲固定格式。考慮使用 ImageMagick MVNRepository

文件存儲位置

  1. 將文件存儲在不同的主機上。允許在爲用戶提供服務的應用程序與處理文件上傳及其存儲的主機之間實現完全的職責分離。有關更多信息,請查看資源。
  2. 將文件存儲在 web root 之外,只允許管理員訪問。如需公開訪問文件,可使用應用程序內映射到文件名的處理程序(someid -> file.ext)

參考

今天先到這兒,希望對雲原生,技術領導力, 企業管理,系統架構設計與評估,團隊管理, 項目管理, 產品管理,信息安全,團隊建設 有參考作用 , 您可能感興趣的文章:
構建創業公司突擊小團隊
國際化環境下系統架構演化
微服務架構設計
視頻直播平臺的系統架構演化
微服務與Docker介紹
Docker與CI持續集成/CD
互聯網電商購物車架構演變案例
互聯網業務場景下消息隊列架構
互聯網高效研發團隊管理演進之一
消息系統架構設計演進
互聯網電商搜索架構演化之一
企業信息化與軟件工程的迷思
企業項目化管理介紹
軟件項目成功之要素
人際溝通風格介紹一
精益IT組織與分享式領導
學習型組織與企業
企業創新文化與等級觀念
組織目標與個人目標
初創公司人才招聘與管理
人才公司環境與企業文化
企業文化、團隊文化與知識共享
高效能的團隊建設
項目管理溝通計劃
構建高效的研發與自動化運維
某大型電商雲平臺實踐
互聯網數據庫架構設計思路
IT基礎架構規劃方案一(網絡系統規劃)
餐飲行業解決方案之客戶分析流程
餐飲行業解決方案之採購戰略制定與實施流程
餐飲行業解決方案之業務設計流程
供應鏈需求調研CheckList
企業應用之性能實時度量系統演變

如有想了解更多軟件設計與架構, 系統IT,企業信息化, 團隊管理 資訊,請關注我的微信訂閱號:

image_thumb2_thumb_thumb_thumb_thumb[1]

作者:Petter Liu
出處:http://www.cnblogs.com/wintersun/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。 該文章也同時發佈在我的獨立博客中-Petter Liu Blog。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章