Uploading files is a standard feature of contemporary web apps. Users often require this capability for submitting documents or posting a profile picture. Here's the catch, though. An enormous security risk might arise from improper file upload management. Attackers may try route traversal, upload malicious files, or even launch denial-of-service attacks.

ASP.NET Core MVC secure file upload handling will be covered in this post along with best practices, sample code, and detailed instructions.

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:

  • Restrict file size and types
  • Validate extensions & MIME type
  • Store outside. wwwroot
  • Use safe filenames

You can provide reliable and secure upload capabilities in your ASP.NET Core MVC applications by adhering to these guidelines.