using DomainResults.Common; using ExtensionMethods; namespace WeatherForecast.Services.Abstractions { /// /// /// public enum FileCategory { /// /// /// Document, /// /// /// Image, /// /// /// Video } /// /// /// public class FileSignature { /// /// /// public List Signatures { get; private set; } /// /// /// public string ContentType { get; private set; } /// /// /// public FileCategory FileCategory { get; private set; } /// /// /// /// /// /// public FileSignature(List signatures, string contentType, FileCategory fileCategory) { Signatures = signatures; ContentType = contentType; FileCategory = fileCategory; } } /// /// /// public abstract class FileServiceBase { /// /// https://gist.github.com/qti3e/6341245314bf3513abb080677cd1c93b /// private readonly Dictionary _data = new() { #region Documents { ".pdf", new FileSignature(new List { "0,25504446" }, "application/pdf", FileCategory.Document) }, { ".rtf", new FileSignature(new List { "0,7B5C72746631" }, "application/rtf", FileCategory.Document) }, { ".doc", new FileSignature(new List { "0,0D444F43", "0,CF11E0A1B11AE100", "0,D0CF11E0A1B11AE1", "0,DBA52D00", "512,ECA5C100" }, "application/msword", FileCategory.Document) }, { ".xls", new FileSignature(new List { "512,0908100000060500", "0,D0CF11E0A1B11AE1", "512,FDFFFFFF04", "512,FDFFFFFF20000000" }, "application/vnd.ms-excel", FileCategory.Document) }, { ".ppt", new FileSignature(new List { "512,006E1EF0", "512,0F00E803", "512,A0461DF0", "0,D0CF11E0A1B11AE1", "512,FDFFFFFF04" }, "application/vnd.ms-powerpoint", FileCategory.Document) }, { ".docx", new FileSignature(new List { "0,504B030414000600" }, "application/vnd.openxmlformats-officedocument.wordprocessingml.document", FileCategory.Document) }, { ".xlsx", new FileSignature(new List { "0,504B030414000600" }, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", FileCategory.Document) }, { ".pptx", new FileSignature(new List { "0,504B030414000600" }, "application/vnd.openxmlformats-officedocument.presentationml.presentation", FileCategory.Document) }, #endregion #region Images { ".jpg", new FileSignature(new List { "0,FFD8" }, "image/jpeg", FileCategory.Image) }, { ".jpeg", new FileSignature(new List { "0,FFD8" }, "image/jpeg", FileCategory.Image) }, { ".jpe", new FileSignature(new List { "0,FFD8" }, "image/jpeg", FileCategory.Image) }, { ".jfif", new FileSignature(new List { "0,FFD8" }, "image/jpeg", FileCategory.Image) }, { ".png", new FileSignature(new List { "0,89504E470D0A1A0A" }, "image/png", FileCategory.Image) }, { ".webp", new FileSignature(new List { "0,52494646" }, "image/webp", FileCategory.Image) } #endregion }; /// /// Don't rely on or trust the FileName property without validation. /// /// /// /// /// private protected (FileCategory?, IDomainResult) CheckFileSignature(string fileName, byte[] bytes, string contentType) { var data = _data.SingleOrDefault(x => x.Key == Path.GetExtension(fileName).ToLower()).Value; if (data == null) return IDomainResult.NotFound(); // check content type if (contentType != data.ContentType) return IDomainResult.Failed(); // check signatures foreach (var signature in data.Signatures) { var splitString = signature.Split(","); var offset = splitString[0].ToInt(); var signBytes = Enumerable.Range(0, splitString[1].Length) .Where(x => x % 2 == 0) .Select(x => Convert.ToByte(splitString[1].Substring(x, 2), 16)) .ToArray(); var sample = new byte[signBytes.Length]; Buffer.BlockCopy(bytes, offset, sample, 0, signBytes.Length); int x = sample.Length ^ signBytes.Length; // https://www.syncfusion.com/succinctly-free-ebooks/application-security-in-net-succinctly/comparing-byte-arrays for (int i = 0; i < sample.Length && i < signBytes.Length; ++i) { x |= sample[i] ^ signBytes[i]; } if (x == 0) return IDomainResult.Success(data.FileCategory); ; } return IDomainResult.Failed(); ; } } }