Introduction
File uploads are a common requirement in modern web applications. From uploading a profile picture to submitting documents, users frequently need this feature.
But here’s the catch. Improper file upload handling can become a huge security risk. Attackers may upload malicious files, attempt path traversal, or even perform denial-of-service attacks.
In this article, we’ll explore secure file upload handling in ASP.NET Core MVC with best practices, sample code, and step-by-step guidance.
Why Is File Upload Security Important?
Insecure file uploads can lead to:
Malware Upload—Hackers upload .exe
,.js
files disguised as images.
Path Traversal – Using filenames like ../../web.config
to overwrite sensitive files.
Denial of Service (DoS)—Uploading very large files to exhaust server resources.
MIME Type Spoofing – Uploading dangerous files renamed as .jpg
or .png
.
Unauthorized Access – Files stored in wwwroot
are directly accessible via URL.
Therefore, a multi-layered defense is critical.
Best Practices for Secure File Uploads
Here are the golden rules for handling file uploads in ASP.NET Core MVC:
Limit File Size – Prevent oversized uploads.
Restrict File Types—Allow only specific file extensions.
Use Safe Filenames—Never trust user-supplied filenames.
Store Outside wwwroot
– Prevent direct public access.
Validate File Content—Don’t rely solely on extensions.
Scan Files (Optional)—Use antivirus or third-party scanning APIs.
Use HTTPS—Ensure secure transport during upload.
Step-by-Step Implementation in ASP.NET Core MVC
Let’s implement secure file upload handling.
Step 1: Configure Maximum File Size
In Program.cs, set the maximum upload size:
using Microsoft.AspNetCore.Http.Features;
var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<FormOptions>(options =>
{
options.MultipartBodyLengthLimit = 10 * 1024 * 1024; // 10 MB limit
});
var app = builder.Build();
app.Run();
Step 2: Create the Upload Model
using System.ComponentModel.DataAnnotations;
public class FileUploadViewModel
{
[Required]
public IFormFile File { get; set; }
}
Step 3: Create the Controller
using Microsoft.AspNetCore.Mvc;
public class FileUploadController : Controller
{
private readonly long _fileSizeLimit = 10 * 1024 * 1024; // 10 MB
private readonly string[] _permittedExtensions = { ".jpg", ".png", ".pdf" };
private readonly string _targetFilePath;
public FileUploadController(IWebHostEnvironment env)
{
// Store files securely outside wwwroot
_targetFilePath = Path.Combine(env.ContentRootPath, "SecureUploads");
if (!Directory.Exists(_targetFilePath))
{
Directory.CreateDirectory(_targetFilePath);
}
}
[HttpGet]
public IActionResult Index() => View();
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Upload(FileUploadViewModel model)
{
if (model.File == null || model.File.Length == 0)
{
ModelState.AddModelError("File", "Please select a file.");
return View("Index");
}
// Validate file size
if (model.File.Length > _fileSizeLimit)
{
ModelState.AddModelError("File", "File size exceeds the limit.");
return View("Index");
}
// Validate extension
var ext = Path.GetExtension(model.File.FileName).ToLowerInvariant();
if (string.IsNullOrEmpty(ext) || !_permittedExtensions.Contains(ext))
{
ModelState.AddModelError("File", "Invalid file type.");
return View("Index");
}
// Generate safe filename
var trustedFileName = Path.GetRandomFileName() + ext;
var filePath = Path.Combine(_targetFilePath, trustedFileName);
// Save securely
using (var stream = new FileStream(filePath, FileMode.Create))
{
await model.File.CopyToAsync(stream);
}
ViewBag.Message = "File uploaded successfully!";
return View("Index");
}
}
Step 4: Create the Razor View
@model FileUploadViewModel
<h2>Secure File Upload in ASP.NET Core MVC</h2>
<form asp-action="Upload" method="post" enctype="multipart/form-data">
<div>
<input type="file" asp-for="File" />
<span asp-validation-for="File" class="text-danger"></span>
</div>
<button type="submit">Upload</button>
</form>
@if (ViewBag.Message != null)
{
<div class="alert alert-success">@ViewBag.Message</div>
}
Additional Security Enhancements
Validate File Signature (Magic Numbers)
Instead of trusting extensions, check file headers.
Quarantine Uploads
Save files temporarily before scanning.
Logging & Monitoring
Log all uploads for auditing.
Rate Limiting
Prevent abuse from excessive uploads.
Conclusion
Secure file upload handling in ASP.NET Core MVC requires layered security:
By following these practices, you can build robust and secure upload functionality in your ASP.NET Core MVC applications.