SecureFileUpload.AspNetCore
1.0.4
See the version list below for details.
dotnet add package SecureFileUpload.AspNetCore --version 1.0.4
NuGet\Install-Package SecureFileUpload.AspNetCore -Version 1.0.4
<PackageReference Include="SecureFileUpload.AspNetCore" Version="1.0.4" />
<PackageVersion Include="SecureFileUpload.AspNetCore" Version="1.0.4" />
<PackageReference Include="SecureFileUpload.AspNetCore" />
paket add SecureFileUpload.AspNetCore --version 1.0.4
#r "nuget: SecureFileUpload.AspNetCore, 1.0.4"
#:package SecureFileUpload.AspNetCore@1.0.4
#addin nuget:?package=SecureFileUpload.AspNetCore&version=1.0.4
#tool nuget:?package=SecureFileUpload.AspNetCore&version=1.0.4
SecureFileUpload.AspNetCore
Drop-in ASP.NET Core 8 protection for file uploads. Register it once and multipart, raw binary, and marked base64 uploads are validated before controller code stores or processes the file.
Security Checks
The package validates the common upload attack paths:
- File name safety: Gujarati/English names up to 255 characters; allows letters, numbers, spaces,
.,-,_,(,); rejects path traversal, null bytes, reserved device names, bidi spoofing, and dangerous double extensions. - Extension policy: blocklist-first by default. Leave
AllowedFileTypesempty for general uploads, or set it per input throughCheckFile. - File size: per-file
MaxFileSizeBytesand request-levelMaxRequestBodySizeBytes. - Magic number/signature: catches renamed files such as
malware.exerenamed tophoto.jpg; PDFs are checked forstartxref/%%EOF, and raster images are checked for clean end markers and unexpected trailing bytes. - MIME cross-check: clearly wrong MIME values are rejected; empty/generic browser MIME values are treated as inconclusive to avoid false positives.
- Image dimensions: catches small image files that declare unsafe width/height/pixel counts.
- Polyglot/script detection: detects executable headers, script markers, and payload bytes appended after valid file content. Raster image compressed data is not scanned as plain text; only headers and appended trailer payloads are inspected to avoid false positives.
- Archive safety: recursive zip/docx/xlsx/pptx inspection, blocked entry extensions, executable/script content inside archives, nested archive depth, compression ratio, entry count, expanded size, corrupt archive, unreadable/encrypted entries, and zip-slip paths.
- Threat scanner hook: host apps can register
IFileThreatScannerfor ClamAV/cloud AV scanning.
The built-in threat scanner is a no-op so existing apps do not break. For high-risk systems, register ClamAV or a cloud malware scanner before files are stored or made available to other users.
Setup
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddSecureFileUpload(builder.Configuration);
builder.WebHost.ConfigureKestrel(o =>
o.Limits.MaxRequestBodySize =
builder.Configuration.GetValue<long>("SecureFileUpload:MaxRequestBodySizeBytes"));
var app = builder.Build();
app.UseRouting();
app.UseSecureFileUpload();
app.MapControllers();
app.Run();
Configuration
{
"SecureFileUpload": {
"Enabled": true,
"AllowedFileTypes": [],
"BlockedExtensions": [ ".exe", ".dll", ".php", ".js", ".sh", ".bat", ".ps1", ".cmd", ".com", ".msi", ".vbs", ".vbe", ".jse", ".wsf", ".jar", ".scr", ".hta", ".lnk", ".reg", ".cpl", ".gadget", ".msc", ".apk", ".app", ".dmg", ".pkg", ".deb", ".rpm", ".docm", ".xlsm", ".pptm", ".svg", ".html", ".htm", ".xhtml" ],
"MaxFileSizeBytes": 5242880,
"MaxRequestBodySizeBytes": 26214400,
"RequireFileExtension": false,
"MaxFileNameLength": 255,
"EnforceImageDimensionCheck": true,
"MaxImageWidth": 10000,
"MaxImageHeight": 10000,
"MaxImagePixels": 50000000,
"EnforceSignatureCheck": true,
"EnforceMimeTypeCheck": true,
"EnforcePolyglotCheck": true,
"BlockZipArchivesWithHighCompressionRatio": true,
"MaxZipCompressionRatio": 100,
"MaxArchiveEntries": 1000,
"MaxArchiveUncompressedBytes": 209715200,
"MaxArchiveDepth": 3,
"MaxArchiveEntryBytes": 52428800,
"BlockArchivesContainingUnsafeFiles": true,
"BlockUnreadableArchiveEntries": true,
"EnableThreatScanner": true,
"MaxFilesPerRequest": 10,
"ExcludedPaths": [ "/api/admin/import" ]
}
}
Per-Input Server Validation
Use CheckFileAsync when one upload input has its own file type and size rule.
The result includes file name, field name, extension, content type, size, error
codes, and ErrorMessage.
using SecureFileUpload.Models;
using SecureFileUpload.Validation;
public class DocumentsController : Controller
{
private readonly IFileValidator _fileValidator;
public DocumentsController(IFileValidator fileValidator)
{
_fileValidator = fileValidator;
}
[HttpPost]
public async Task<IActionResult> Upload(IFormFile document)
{
var result = await _fileValidator.CheckFileAsync(
document,
new FileCheckOptions
{
FieldName = nameof(document),
AllowedFileTypes = new[] { ".pdf", ".jpg", ".jpeg", ".png" },
MaxFileSizeBytes = FileCheckOptions.Megabytes(5)
});
if (!result.IsValid)
{
return BadRequest(result);
}
return Ok();
}
}
Short overload:
var result = await _fileValidator.CheckFileAsync(
document,
new[] { ".pdf", ".jpg", ".png" },
FileCheckOptions.Megabytes(5));
For byte-array/API/base64 workflows:
var result = _fileValidator.CheckFile(
fileName,
declaredContentType,
bytes,
new[] { ".pdf", ".jpg" },
FileCheckOptions.Megabytes(2));
Client Toast and CheckFile Helper
The package ships optional static web assets:
<link rel="stylesheet" href="~/_content/SecureFileUpload.AspNetCore/css/secure-upload-toast.css" />
<script src="~/_content/SecureFileUpload.AspNetCore/js/secure-upload-client.js"></script>
Developer-side input validation:
const result = window.SecureUpload.CheckFile("#Document", ".pdf,.jpg,.png", 5);
if (!result.isValid) {
console.log(result.message);
}
5 means 5 MB by default. You can also pass bytes:
window.SecureUpload.CheckFile("#Document", [".pdf", ".jpg"], 5242880, {
sizeUnit: "bytes"
});
Automatic validation through markup:
<input type="file"
id="Document"
accept=".pdf,.jpg,.png"
data-secure-upload-size="5"
multiple />
The helper shows the same toast style and returns the full message with file info.
The client understands both camelCase and PascalCase server validation payloads,
so older package responses like Success/Message/Files do not leak raw JSON into
the toast.
Base64 JSON Uploads
public class UploadDocumentRequest
{
public string DocumentName { get; set; } = string.Empty;
[Base64File(FileNameProperty = nameof(DocumentName))]
public string FileContent { get; set; } = string.Empty;
}
Malware Scanner Hook
Register your own scanner to replace the default no-op scanner:
using SecureFileUpload.Scanning;
builder.Services.AddSingleton<IFileThreatScanner, ClamAvFileThreatScanner>();
builder.Services.AddSecureFileUpload(builder.Configuration);
Return FileThreatScanResult.Unsafe("ThreatName") to block the upload with a
stable MALWARE_DETECTED error code. If the scanner throws, the validator fails
closed with MALWARE_SCAN_FAILED.
Example Rejection Response
{
"success": false,
"message": "One or more uploaded files failed validation.",
"files": [
{
"fieldName": "document",
"fileName": "invoice.jpg",
"extension": ".jpg",
"declaredContentType": "image/jpeg",
"fileSizeBytes": 14804,
"isValid": false,
"errorMessage": "File content does not match the expected binary signature for a '.jpg' file. The extension may have been spoofed.",
"errors": [
{
"code": "SIGNATURE_MISMATCH",
"message": "File content does not match the expected binary signature for a '.jpg' file. The extension may have been spoofed."
}
]
}
]
}
Building the Package
dotnet pack -c Release
The package is written to bin/Release/SecureFileUpload.AspNetCore.<version>.nupkg.
Run the no-dependency smoke validation matrix:
dotnet run --project ..\SecureFileUpload.Tests\SecureFileUpload.Tests.csproj -c Release
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Adds IFileValidator.CheckFile/CheckFileAsync for per-input file type and size
validation, richer validation result metadata, blocklist-first defaults, MIME
false-positive fixes, recursive archive inspection, image dimension limits,
a malware scanner hook, PNG/JPEG/GIF/BMP/WebP false-positive fixes, basic
PDF corruption and trailing-byte detection, raster image end-marker
validation, 255-character Gujarati/English filename support, an Enabled
switch, and packaged toast/client helper assets.