C# 驗證檔案類型 & 讀取檔案標頭 & Stream 多次讀取 | C# validate file type & read file header bytes & Stream read multiple times
最近遇到一個防呆驗證問題,
一個檔案上傳的功能,即使有了副檔名偵測,也會有可能遇到有心人故意弄非正規檔案傳入,
那該怎麼在後端去防呆呢?
找到了以下這篇Wiki,統整了大部分檔案的File Signature (檔案特徵)
List of file signatures
其中Hex signature欄位是主要需要的資訊,可以利用讀取檔案的前幾個Byte,然後比對公版的檔案特徵值,判斷是否符合來防呆這個檔案不是允許的
這裡我只需要用到JPG、GIF、PNG的標頭就好
要注意這份清單是不固定的,可能會誕生新的格式,或者有漏,但是就是一個參考,也可以去個別檔案的規格文件查看file signature章節來設定
有些檔案會有多種標頭格式,我找到的驗證做法是取前面幾個相同的部分驗證,後面不同的部分就不驗證
例如jpg就有四種:FF D8 FF DBFF D8 FF E0 00 10 4A 46 49 46 00 01FF D8 FF EEFF D8 FF E1 ?? ?? 45 78 69 66 00 00
所以乾脆只比對前面的FF D8
實作的話則是參考這篇stackoverflow解答:
Validate image from file in C#
主要概念是把檔案的Byte傳入方法後,根據設定好的規格長度取出byte,用SequenceEqual比對是否相符
我先建立一個通用的圖檔類型enum:
1 | public enum ValidImageTypeEnum |
然後作一個根據這個enum回傳指定的byte array的方法
1 | public static IEnumerable<byte[]> GetImageValidateHeaders(ValidImageTypeEnum[] fileTypes) |
這裡用16進位是方便比對wiki清單,原本解答中的是放10進位的數字
再做一個把圖片byte array傳入,比對是否為圖片格式的header擴充方法
1 | public static bool IsImage(this byte[] fileBytes, IEnumerable<byte[]> headers) |
然後使用上就是當你在操作Stream時把前幾個byte讀取出來後送來驗證
1 | IEnumerable<byte[]> validImageFileHeaders = |
再來就是一個很重要的
1 | stream.Position = 0; |
我在判斷完檔案標頭之後一直遇到圖檔上傳都變空的,查了一下才知道,
因為Stream在讀取時會改變他的讀取位置,
所以後面要繼續使用同一個Stream時必須重設位置,這樣後面在讀取時才不會從中間或尾巴讀取導致變空的檔案
這個問題卡了一小時,關鍵字不太會下…