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,.jsfiles disguised as images.Path Traversal – Using filenames like
../../web.configto overwrite sensitive files.Denial of Service (DoS)—Uploading very large files to exhaust server resources.
MIME Type Spoofing – Uploading dangerous files renamed as
.jpgor.png.Unauthorized Access – Files stored in
wwwrootare 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:
Restrict file size and types
Validate extensions & MIME type
Store outside.
wwwrootUse safe filenames
By following these practices, you can build robust and secure upload functionality in your ASP.NET Core MVC applications.

Join the conversation! Your thoughts help the community grow.