SecureFileUpload.AspNetCore
1.0.6
dotnet add package SecureFileUpload.AspNetCore --version 1.0.6
NuGet\Install-Package SecureFileUpload.AspNetCore -Version 1.0.6
<PackageReference Include="SecureFileUpload.AspNetCore" Version="1.0.6" />
<PackageVersion Include="SecureFileUpload.AspNetCore" Version="1.0.6" />
<PackageReference Include="SecureFileUpload.AspNetCore" />
paket add SecureFileUpload.AspNetCore --version 1.0.6
#r "nuget: SecureFileUpload.AspNetCore, 1.0.6"
#:package SecureFileUpload.AspNetCore@1.0.6
#addin nuget:?package=SecureFileUpload.AspNetCore&version=1.0.6
#tool nuget:?package=SecureFileUpload.AspNetCore&version=1.0.6
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. - 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 and script markers. 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. - PDF inspection: iText 8.0.5 (also used by the host app) resolves PDF objects, escaped names, indirect lengths and compressed object tables. Binary stream data is excluded from generic text matching; JavaScript and launch actions are rejected. Non-stream bytes, including appended payloads, retain script and executable checks. This also applies to PDFs inside archives.
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
using SecureFileUpload.Extensions;
using SecureFileUpload.Options;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddSecureFileUpload(builder.Configuration);
var upload = builder.Configuration.GetSection(SecureFileUploadOptions.SectionName)
.Get<SecureFileUploadOptions>() ?? new SecureFileUploadOptions();
if (upload.Enabled)
{
builder.WebHost.ConfigureKestrel(o => o.Limits.MaxRequestBodySize = upload.MaxRequestBodySizeBytes);
builder.Services.Configure<IISServerOptions>(o => o.MaxRequestBodySize = upload.MaxRequestBodySizeBytes);
}
var app = builder.Build();
app.UseRouting();
app.UseSecureFileUpload();
app.MapControllers();
app.Run();
Configuration
Register the services and middleware even when Enabled is false: the middleware
and MVC filter honor the switch themselves. Settings are read at application startup;
restart after changing them. Multipart parser limits are configured by
AddSecureFileUpload. IIS request filtering (maxAllowedContentLength) and reverse
proxy limits still need to permit the configured request size.
An explicit BlockedExtensions array replaces the defaults, including an empty array.
AllowedFileTypes accepts extensions, MIME types and MIME wildcards such as
["image/*", "application/pdf"]. MIME matching uses the package's known extensions;
it does not trust the uploaded Content-Type header. Signature/content checks and
the blocklist still apply. The supported raster formats are JPEG, PNG, GIF, BMP and
WebP; SVG remains blocked by default.
For the client helper, render data-enabled="false" on its script element when
server validation is disabled. This disables interception and the client toggle.
{
"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
For an entire endpoint, apply the following attribute (middleware must run after
UseRouting). It restricts all multipart/raw files in addition to the global policy,
so an endpoint cannot broaden the global allowlist or increase the global size limit:
[HttpPost]
[SecureFileUpload.Filters.UploadFileTypes("image/*,application/pdf", MaxFileSizeBytes = 1024 * 1024)]
public IActionResult Upload(IFormFile document) => Ok();
Use accept="image/*,application/pdf" data-secure-upload-size="1" on the file input
for the same browser hint and client size check. The server attribute is required
because browsers do not send the HTML accept attribute with uploads.
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.
The web application references the local SecureFileUpload project so builds
include the corrected validator. Other consumers can use package version 1.0.5.
Run the smoke validation matrix (PDF fixtures use iText and its crypto adapter):
dotnet run --project ..\SecureFileUpload.Tests\SecureFileUpload.Tests.csproj -c Release
To assert that a customer sample passes both the validator and multipart middleware:
dotnet run --project ..\SecureFileUpload.Tests\SecureFileUpload.Tests.csproj -c Release -- --expect-valid "D:\Sample_script_pdf\virumidiaBillNo28.pdf"
| 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
- itext7 (>= 8.0.5)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Accepts scanner-generated PDFs with a recoverable incorrect metadata stream
length while continuing to inspect PDF actions and non-stream payloads.
Uses PDF object inspection to avoid script false positives in compressed
image streams while rejecting PDF JavaScript and launch actions.
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 detection, 255-character Gujarati/English filename support,
an Enabled switch, and packaged toast/client helper assets.