檔案上傳漏洞防範-檔案型別檢測

PetterLiu發表於2024-05-01

檔案上傳漏洞防範-檔案型別檢測

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

為了防止使用者上傳文字檔案以外的檔案,我們可以在前端進行操作。

<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)

參考

  • MIME types (IANA media types)
  • Form Content-Type
  • List of file signatures
  • Apache Tika - Content Detection

今天先到這兒,希望對雲原生,技術領導力, 企業管理,系統架構設計與評估,團隊管理, 專案管理, 產品管理,資訊保安,團隊建設 有參考作用 , 您可能感興趣的文章:
構建創業公司突擊小團隊
國際化環境下系統架構演化
微服務架構設計
影片直播平臺的系統架構演化
微服務與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。

相關文章