reactredux/webapi/Services/FileSecurityService/FileSecurityService.cs

175 lines
4.9 KiB
C#

using Core.Enumerations;
using DomainResults.Common;
using ExtensionMethods;
using Extensions;
namespace FileSecurityService {
/// <summary>
///
/// </summary>
public interface IFileSecurityService {
/// <summary>
///
/// </summary>
/// <param name="fileName"></param>
/// <param name="bytes"></param>
/// <param name="contentType"></param>
/// <returns></returns>
(MediaTypes?, IDomainResult) CheckFileSignature(string fileName, byte[] bytes, string contentType);
}
public class FileSecurityService : IFileSecurityService {
/// <summary>
/// https://gist.github.com/qti3e/6341245314bf3513abb080677cd1c93b
/// </summary>
private readonly Dictionary<string, FileSignature> _data;
/// <summary>
///
/// </summary>
public FileSecurityService() {
_data = new Dictionary<string, FileSignature>() {
#region Documents
{
".pdf",
new FileSignature(new List<string> {
"0,25504446"
}, "application/pdf", MediaTypes.Document)
},
{
".rtf",
new FileSignature(new List<string> {
"0,7B5C72746631"
}, "application/rtf", MediaTypes.Document)
},
{
".doc",
new FileSignature(new List<string> {
"0,0D444F43",
"0,CF11E0A1B11AE100",
"0,D0CF11E0A1B11AE1",
"0,DBA52D00",
"512,ECA5C100"
}, "application/msword", MediaTypes.Document)
},
{
".xls",
new FileSignature(new List<string> {
"512,0908100000060500",
"0,D0CF11E0A1B11AE1",
"512,FDFFFFFF04",
"512,FDFFFFFF20000000"
}, "application/vnd.ms-excel", MediaTypes.Document)
},
{
".ppt",
new FileSignature(new List<string> {
"512,006E1EF0",
"512,0F00E803",
"512,A0461DF0",
"0,D0CF11E0A1B11AE1",
"512,FDFFFFFF04"
}, "application/vnd.ms-powerpoint", MediaTypes.Document)
},
{
".docx",
new FileSignature(new List<string> {
"0,504B030414000600"
}, "application/vnd.openxmlformats-officedocument.wordprocessingml.document", MediaTypes.Document)
},
{
".xlsx",
new FileSignature(new List<string> {
"0,504B030414000600"
}, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", MediaTypes.Document)
},
{
".pptx",
new FileSignature(new List<string> {
"0,504B030414000600"
}, "application/vnd.openxmlformats-officedocument.presentationml.presentation", MediaTypes.Document)
},
#endregion
#region Images
{
".jpg",
new FileSignature(new List<string> {
"0,FFD8"
}, "image/jpeg", MediaTypes.Image)
},
{
".jpeg",
new FileSignature(new List<string> {
"0,FFD8"
}, "image/jpeg", MediaTypes.Image)
},
{
".jpe",
new FileSignature(new List<string> {
"0,FFD8"
}, "image/jpeg", MediaTypes.Image)
},
{
".jfif",
new FileSignature(new List<string> {
"0,FFD8"
}, "image/jpeg", MediaTypes.Image)
},
{
".png",
new FileSignature(new List<string> {
"0,89504E470D0A1A0A"
}, "image/png", MediaTypes.Image)
},
{
".webp",
new FileSignature(new List<string> {
"0,52494646"
}, "image/webp", MediaTypes.Image)
}
#endregion
};
}
/// <summary>
/// Don't rely on or trust the FileName property without validation.
/// </summary>
/// <param name="fileName"></param>
/// <param name="bytes"></param>
/// <param name="contentType"></param>
/// <returns></returns>
public (MediaTypes?, 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<MediaTypes?>();
// check content type
if (contentType != data.ContentType)
return IDomainResult.Failed<MediaTypes?>();
// 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);
if (sample.XORCcomparison(signBytes)) return IDomainResult.Success(data.MediaType);
}
return IDomainResult.Failed<MediaTypes?>();
}
}
}