(feat): file security service
This commit is contained in:
parent
d6fcf34bf6
commit
2c137947c6
@ -0,0 +1,10 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace FileSecurityService.Extensions
|
||||
{
|
||||
public static class ServiceCollectionExtensions {
|
||||
public static void RegisterFileSecurityService(this IServiceCollection services) {
|
||||
services.AddSingleton<IFileSecurityService, FileSecurityService>();
|
||||
}
|
||||
}
|
||||
}
|
||||
28
webapi/Services/FileSecurityService/FileCategory.cs
Normal file
28
webapi/Services/FileSecurityService/FileCategory.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FileSecurityService {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public enum FileCategory {
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
Document,
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
Image,
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
Video
|
||||
}
|
||||
}
|
||||
@ -1,174 +1,138 @@
|
||||
using DomainResults.Common;
|
||||
using ExtensionMethods;
|
||||
|
||||
namespace WeatherForecast.Services.Abstractions {
|
||||
namespace FileSecurityService {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public enum FileCategory {
|
||||
public interface IFileSecurityService {
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
Document,
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
Image,
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
Video
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class FileSignature {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public List<string> Signatures { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string ContentType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public FileCategory FileCategory { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="signatures"></param>
|
||||
/// <param name="fileName"></param>
|
||||
/// <param name="bytes"></param>
|
||||
/// <param name="contentType"></param>
|
||||
/// <param name="fileCategory"></param>
|
||||
public FileSignature(List<string> signatures, string contentType, FileCategory fileCategory) {
|
||||
Signatures = signatures;
|
||||
ContentType = contentType;
|
||||
FileCategory = fileCategory;
|
||||
}
|
||||
/// <returns></returns>
|
||||
(FileCategory?, IDomainResult) CheckFileSignature(string fileName, byte[] bytes, string contentType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public abstract class FileServiceBase {
|
||||
|
||||
public class FileSecurityService : IFileSecurityService {
|
||||
/// <summary>
|
||||
/// https://gist.github.com/qti3e/6341245314bf3513abb080677cd1c93b
|
||||
/// </summary>
|
||||
private readonly Dictionary<string, FileSignature> _data = new() {
|
||||
#region Documents
|
||||
{
|
||||
".pdf",
|
||||
new FileSignature(new List<string> {
|
||||
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", FileCategory.Document)
|
||||
},
|
||||
{
|
||||
".rtf",
|
||||
new FileSignature(new List<string> {
|
||||
},
|
||||
{
|
||||
".rtf",
|
||||
new FileSignature(new List<string> {
|
||||
"0,7B5C72746631"
|
||||
}, "application/rtf", FileCategory.Document)
|
||||
},
|
||||
{
|
||||
".doc",
|
||||
new FileSignature(new List<string> {
|
||||
},
|
||||
{
|
||||
".doc",
|
||||
new FileSignature(new List<string> {
|
||||
"0,0D444F43",
|
||||
"0,CF11E0A1B11AE100",
|
||||
"0,D0CF11E0A1B11AE1",
|
||||
"0,DBA52D00",
|
||||
"512,ECA5C100"
|
||||
}, "application/msword", FileCategory.Document)
|
||||
},
|
||||
{
|
||||
".xls",
|
||||
new FileSignature(new List<string> {
|
||||
},
|
||||
{
|
||||
".xls",
|
||||
new FileSignature(new List<string> {
|
||||
"512,0908100000060500",
|
||||
"0,D0CF11E0A1B11AE1",
|
||||
"512,FDFFFFFF04",
|
||||
"512,FDFFFFFF20000000"
|
||||
}, "application/vnd.ms-excel", FileCategory.Document)
|
||||
},
|
||||
{
|
||||
".ppt",
|
||||
new FileSignature(new List<string> {
|
||||
},
|
||||
{
|
||||
".ppt",
|
||||
new FileSignature(new List<string> {
|
||||
"512,006E1EF0",
|
||||
"512,0F00E803",
|
||||
"512,A0461DF0",
|
||||
"0,D0CF11E0A1B11AE1",
|
||||
"512,FDFFFFFF04"
|
||||
}, "application/vnd.ms-powerpoint", FileCategory.Document)
|
||||
},
|
||||
{
|
||||
".docx",
|
||||
new FileSignature(new List<string> {
|
||||
},
|
||||
{
|
||||
".docx",
|
||||
new FileSignature(new List<string> {
|
||||
"0,504B030414000600"
|
||||
}, "application/vnd.openxmlformats-officedocument.wordprocessingml.document", FileCategory.Document)
|
||||
},
|
||||
{
|
||||
".xlsx",
|
||||
new FileSignature(new List<string> {
|
||||
},
|
||||
{
|
||||
".xlsx",
|
||||
new FileSignature(new List<string> {
|
||||
"0,504B030414000600"
|
||||
}, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", FileCategory.Document)
|
||||
},
|
||||
{
|
||||
".pptx",
|
||||
new FileSignature(new List<string> {
|
||||
},
|
||||
{
|
||||
".pptx",
|
||||
new FileSignature(new List<string> {
|
||||
"0,504B030414000600"
|
||||
}, "application/vnd.openxmlformats-officedocument.presentationml.presentation", FileCategory.Document)
|
||||
},
|
||||
#endregion
|
||||
},
|
||||
#endregion
|
||||
|
||||
#region Images
|
||||
{
|
||||
".jpg",
|
||||
new FileSignature(new List<string> {
|
||||
#region Images
|
||||
{
|
||||
".jpg",
|
||||
new FileSignature(new List<string> {
|
||||
"0,FFD8"
|
||||
}, "image/jpeg", FileCategory.Image)
|
||||
},
|
||||
{
|
||||
".jpeg",
|
||||
new FileSignature(new List<string> {
|
||||
},
|
||||
{
|
||||
".jpeg",
|
||||
new FileSignature(new List<string> {
|
||||
"0,FFD8"
|
||||
}, "image/jpeg", FileCategory.Image)
|
||||
},
|
||||
{
|
||||
".jpe",
|
||||
new FileSignature(new List<string> {
|
||||
},
|
||||
{
|
||||
".jpe",
|
||||
new FileSignature(new List<string> {
|
||||
"0,FFD8"
|
||||
}, "image/jpeg", FileCategory.Image)
|
||||
},
|
||||
{
|
||||
".jfif",
|
||||
new FileSignature(new List<string> {
|
||||
},
|
||||
{
|
||||
".jfif",
|
||||
new FileSignature(new List<string> {
|
||||
"0,FFD8"
|
||||
}, "image/jpeg", FileCategory.Image)
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
".png",
|
||||
new FileSignature(new List<string> {
|
||||
{
|
||||
".png",
|
||||
new FileSignature(new List<string> {
|
||||
"0,89504E470D0A1A0A"
|
||||
}, "image/png", FileCategory.Image)
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
".webp",
|
||||
new FileSignature(new List<string> {
|
||||
{
|
||||
".webp",
|
||||
new FileSignature(new List<string> {
|
||||
"0,52494646"
|
||||
}, "image/webp", FileCategory.Image)
|
||||
}
|
||||
#endregion
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Don't rely on or trust the FileName property without validation.
|
||||
@ -177,7 +141,7 @@ namespace WeatherForecast.Services.Abstractions {
|
||||
/// <param name="bytes"></param>
|
||||
/// <param name="contentType"></param>
|
||||
/// <returns></returns>
|
||||
private protected (FileCategory?, IDomainResult) CheckFileSignature(string fileName, byte[] bytes, string contentType) {
|
||||
public (FileCategory?, IDomainResult) CheckFileSignature(string fileName, byte[] bytes, string contentType) {
|
||||
|
||||
var data = _data.SingleOrDefault(x => x.Key == Path.GetExtension(fileName).ToLower()).Value;
|
||||
|
||||
@ -208,10 +172,10 @@ namespace WeatherForecast.Services.Abstractions {
|
||||
x |= sample[i] ^ signBytes[i];
|
||||
}
|
||||
|
||||
if (x == 0) return IDomainResult.Success(data.FileCategory); ;
|
||||
if (x == 0) return IDomainResult.Success(data.FileCategory);
|
||||
}
|
||||
|
||||
return IDomainResult.Failed<FileCategory?>(); ;
|
||||
return IDomainResult.Failed<FileCategory?>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DomainResult.Common" Version="3.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
|
||||
<PackageReference Include="nClam" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Extensions\Extensions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
40
webapi/Services/FileSecurityService/FileSignature.cs
Normal file
40
webapi/Services/FileSecurityService/FileSignature.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FileSecurityService {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
internal class FileSignature {
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public List<string> Signatures { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string ContentType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public FileCategory FileCategory { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="signatures"></param>
|
||||
/// <param name="contentType"></param>
|
||||
/// <param name="fileCategory"></param>
|
||||
public FileSignature(List<string> signatures, string contentType, FileCategory fileCategory) {
|
||||
Signatures = signatures;
|
||||
ContentType = contentType;
|
||||
FileCategory = fileCategory;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -29,6 +29,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoreTests", "Tests\Core\Cor
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageService", "Services\ImageService\ImageService.csproj", "{16552644-D7EE-4B4A-A725-79909A8103DE}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileSecurityService", "Services\FileSecurityService\FileSecurityService.csproj", "{AD515653-9145-4894-9017-0ABA5A5892F4}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -79,6 +81,10 @@ Global
|
||||
{16552644-D7EE-4B4A-A725-79909A8103DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{16552644-D7EE-4B4A-A725-79909A8103DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{16552644-D7EE-4B4A-A725-79909A8103DE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AD515653-9145-4894-9017-0ABA5A5892F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AD515653-9145-4894-9017-0ABA5A5892F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AD515653-9145-4894-9017-0ABA5A5892F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AD515653-9145-4894-9017-0ABA5A5892F4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@ -89,6 +95,7 @@ Global
|
||||
{43315A1D-9E09-4398-84B9-A9D9D623AE5A} = {216302C2-64DE-4AE7-BC14-BDAC5B732472}
|
||||
{04CB9827-AA6D-4708-A26D-8420C842506D} = {216302C2-64DE-4AE7-BC14-BDAC5B732472}
|
||||
{16552644-D7EE-4B4A-A725-79909A8103DE} = {113EE574-E047-4727-AA36-841F845504D5}
|
||||
{AD515653-9145-4894-9017-0ABA5A5892F4} = {113EE574-E047-4727-AA36-841F845504D5}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {E2805D02-2425-424C-921D-D97341B76F73}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
using DataProviders;
|
||||
using DataProviders.Buckets;
|
||||
using DomainResults.Common;
|
||||
using WeatherForecast.Services.Abstractions;
|
||||
using FileSecurityService;
|
||||
|
||||
namespace WeatherForecast.Services {
|
||||
|
||||
@ -42,21 +42,25 @@ namespace WeatherForecast.Services {
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class FileService : FileServiceBase, IFileService {
|
||||
public class FileService : IFileService {
|
||||
|
||||
private readonly ILogger<FilesService> _logger;
|
||||
private readonly IFileSecurityService _fileSecurityService;
|
||||
private readonly IImagesBucketDataProvider _imageBucketDataProvider;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="fileSecurityService"></param>
|
||||
/// <param name="imageBucketDataProvider"></param>
|
||||
public FileService(
|
||||
ILogger<FilesService> logger,
|
||||
IFileSecurityService fileSecurityService,
|
||||
IImagesBucketDataProvider imageBucketDataProvider
|
||||
) {
|
||||
_logger = logger;
|
||||
_fileSecurityService = fileSecurityService;
|
||||
_imageBucketDataProvider = imageBucketDataProvider;
|
||||
}
|
||||
|
||||
@ -76,8 +80,7 @@ namespace WeatherForecast.Services {
|
||||
file.CopyTo(ms);
|
||||
var bytes = ms.ToArray();
|
||||
|
||||
var (fileCategory, signatureResult) = CheckFileSignature(file.FileName, bytes, file.ContentType);
|
||||
|
||||
var (fileCategory, signatureResult) = _fileSecurityService.CheckFileSignature(file.FileName, bytes, file.ContentType);
|
||||
if(!signatureResult.IsSuccess || fileCategory == null)
|
||||
return IDomainResult.Failed<Guid?>();
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
using DataProviders;
|
||||
using DataProviders.Buckets;
|
||||
using DomainResults.Common;
|
||||
using WeatherForecast.Services.Abstractions;
|
||||
using FileSecurityService;
|
||||
|
||||
namespace WeatherForecast.Services {
|
||||
|
||||
@ -31,9 +31,10 @@ namespace WeatherForecast.Services {
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class FilesService : FileServiceBase, IFilesService {
|
||||
public class FilesService : IFilesService {
|
||||
|
||||
private readonly ILogger<FilesService> _logger;
|
||||
private readonly IFileSecurityService _fileSecurityService;
|
||||
private readonly IImagesBucketDataProvider _imageBucketDataProvider;
|
||||
|
||||
/// <summary>
|
||||
@ -43,9 +44,11 @@ namespace WeatherForecast.Services {
|
||||
/// <param name="imageBucketDataProvider"></param>
|
||||
public FilesService(
|
||||
ILogger<FilesService> logger,
|
||||
IFileSecurityService fileSecurityService,
|
||||
IImagesBucketDataProvider imageBucketDataProvider
|
||||
) {
|
||||
_logger = logger;
|
||||
_fileSecurityService = fileSecurityService;
|
||||
_imageBucketDataProvider = imageBucketDataProvider;
|
||||
}
|
||||
|
||||
@ -67,8 +70,15 @@ namespace WeatherForecast.Services {
|
||||
foreach (var formFile in files) {
|
||||
using var ms = new MemoryStream();
|
||||
formFile.CopyTo(ms);
|
||||
var bytes = ms.ToArray();
|
||||
|
||||
newFiles.Add(new BucketFile(formFile.FileName, ms.ToArray(), formFile.ContentType));
|
||||
var (fileCategory, signatureResult) = _fileSecurityService.CheckFileSignature(formFile.FileName, bytes, formFile.ContentType);
|
||||
if (!signatureResult.IsSuccess || fileCategory == null)
|
||||
return IDomainResult.Failed<List<Guid>?>();
|
||||
|
||||
var bucketFile = new BucketFile(formFile.FileName, bytes, formFile.ContentType);
|
||||
|
||||
newFiles.Add(bucketFile);
|
||||
}
|
||||
|
||||
var (list, result) = _imageBucketDataProvider.UploadMany(siteId, userId, newFiles);
|
||||
|
||||
@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using WeatherForecast.Services;
|
||||
using DataProviders.Extensions;
|
||||
using System.Text.Json.Serialization;
|
||||
using FileSecurityService.Extensions;
|
||||
|
||||
namespace WeatherForecast {
|
||||
|
||||
@ -84,6 +85,7 @@ namespace WeatherForecast {
|
||||
services.AddScoped<IFilesService, FilesService>();
|
||||
|
||||
services.RegisterDataproviders(appSettings);
|
||||
services.RegisterFileSecurityService();
|
||||
|
||||
#region Swagger
|
||||
services.ConfigureSwaggerGen(options => {
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
<ProjectReference Include="..\Core\Core.csproj" />
|
||||
<ProjectReference Include="..\DataProviders\DataProviders.csproj" />
|
||||
<ProjectReference Include="..\Extensions\Extensions.csproj" />
|
||||
<ProjectReference Include="..\Services\FileSecurityService\FileSecurityService.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user