ASP.NET Core  

Building Secure File Upload APIs in ASP.NET Core 10

File uploads are a common requirement in modern web applications. Users upload profile pictures, invoices, contracts, medical reports, spreadsheets, videos, and many other types of documents every day. While implementing file uploads appears straightforward, it is also one of the most common sources of security vulnerabilities.

Attackers frequently exploit insecure upload endpoints by submitting malicious files, oversized payloads, executable content, or files with spoofed extensions. Without proper validation and storage practices, a file upload endpoint can become an entry point for malware distribution, denial-of-service attacks, or even remote code execution.

In this article, you'll learn how to build secure, production-ready file upload APIs in ASP.NET Core 10 by validating uploaded files, enforcing limits, safely storing content, and following security best practices.

Understanding File Upload Risks

Why File Uploads Require Special Attention

A file upload endpoint accepts data directly from external users, making it a high-risk area of any application.

Common threats include:

  • Malware uploads

  • Executable files disguised as images

  • Oversized files causing resource exhaustion

  • Directory traversal attacks

  • Content type spoofing

  • Duplicate file names

  • Unauthorized file access

A secure implementation validates every uploaded file before it reaches permanent storage.

Accepting File Uploads

ASP.NET Core represents uploaded files using the IFormFile interface.

app.MapPost("/upload", async (IFormFile file) =>
{
    if (file.Length == 0)
    {
        return Results.BadRequest("No file uploaded.");
    }

    return Results.Ok();
});

Why Use IFormFile?

IFormFile provides access to the uploaded file's metadata and content stream without requiring the entire file to be loaded into memory immediately.

This makes it suitable for handling uploads efficiently while allowing validation before processing.

Validate File Size

Never allow unlimited uploads.

const long MaxFileSize = 10 * 1024 * 1024;

if (file.Length > MaxFileSize)
{
    return Results.BadRequest(
        "File exceeds the maximum allowed size.");
}

Why Validate Size?

Large uploads consume:

  • Memory

  • CPU

  • Disk space

  • Network bandwidth

Restricting upload size helps prevent resource exhaustion and improves application stability.

Validate File Extensions

Only allow expected file types.

var allowedExtensions = new[]
{
    ".jpg",
    ".jpeg",
    ".png",
    ".pdf"
};

var extension =
    Path.GetExtension(file.FileName)
        .ToLowerInvariant();

if (!allowedExtensions.Contains(extension))
{
    return Results.BadRequest(
        "Unsupported file type.");
}

Why Validate Extensions?

Extension validation blocks many accidental or malicious uploads.

However, file extensions alone should never be trusted because attackers can easily rename executable files.

Extension validation should always be combined with additional verification.

Validate Content Type

Inspect the uploaded MIME type.

var allowedTypes = new[]
{
    "image/jpeg",
    "image/png",
    "application/pdf"
};

if (!allowedTypes.Contains(file.ContentType))
{
    return Results.BadRequest(
        "Invalid content type.");
}

Why Check MIME Types?

Checking the content type provides another layer of validation.

Although MIME types can also be spoofed, combining multiple validation techniques significantly reduces risk.

Generate Safe File Names

Never store files using the original file name.

var fileName =
    $"{Guid.NewGuid()}{extension}";

Why Generate New Names?

Original file names may contain:

  • Duplicate names

  • Unsupported characters

  • Sensitive information

  • Path traversal sequences

Generating unique names avoids collisions while preventing users from influencing server-side file paths.

Save Files Securely

Store uploads outside the web root whenever possible.

var uploads =
    Path.Combine(
        builder.Environment.ContentRootPath,
        "Uploads");

Directory.CreateDirectory(uploads);

var path =
    Path.Combine(uploads, fileName);

await using var stream =
    File.Create(path);

await file.CopyToAsync(stream);

Why Store Outside the Web Root?

Files stored outside wwwroot cannot be downloaded directly through a browser.

Instead, applications should authorize every download request before returning the file.

This prevents unauthorized access to sensitive documents.

End-to-End Implementation

Consider a document management application.

Architecture:

Employee
     │
     ▼
Upload API
     │
File Validation
     │
Virus Scanning
     │
Secure Storage
     │
Metadata Database

Workflow:

  1. A user selects a document.

  2. The API validates file size.

  3. The extension and MIME type are verified.

  4. A unique file name is generated.

  5. The file is scanned for malware.

  6. The file is stored outside the web root.

  7. File metadata is stored in the database.

  8. The API returns the generated document identifier.

This approach separates validation, storage, and metadata management, making the system easier to maintain and secure.

Virus Scanning

Production systems should scan uploaded files before making them available.

Typical workflow:

  1. Receive upload.

  2. Store temporarily.

  3. Scan using antivirus software.

  4. Delete infected files.

  5. Move clean files to permanent storage.

Virus scanning helps protect downstream users from malicious content while reducing organizational risk.

Local Storage vs Cloud Storage

Storage OptionAdvantagesConsiderations
Local DiskSimple setupLimited scalability
Network StorageShared accessAdditional infrastructure
Azure Blob StorageHighly scalableCloud dependency
Amazon S3Durable and scalableRequires cloud integration

For cloud-native applications, object storage services generally provide better scalability and durability than local disks.

Best Practices

  • Validate every uploaded file.

  • Restrict allowed extensions.

  • Verify MIME types.

  • Enforce upload size limits.

  • Generate unique file names.

  • Store files outside the web root.

  • Scan files for malware.

  • Log upload activity.

  • Secure download endpoints with authorization.

Common Mistakes

One common mistake is trusting the file extension supplied by the client. Attackers can easily rename executable files to appear harmless.

Another issue is storing uploaded files directly inside the application's web root. This may allow unauthorized users to access files without proper authorization.

Developers also sometimes load large files entirely into memory before saving them, increasing memory usage and reducing application scalability.

Testing and Validation

Before deploying a file upload API, verify the following:

  • Valid file uploads

  • Invalid extensions

  • Invalid MIME types

  • Oversized uploads

  • Empty file handling

  • Duplicate uploads

  • Unauthorized upload attempts

  • Malware detection workflow

  • Download authorization

Automated integration tests help ensure security measures continue working as the application evolves.

Performance Considerations

Efficient file handling improves scalability.

Consider these recommendations:

  • Stream files instead of buffering large uploads.

  • Apply upload size limits.

  • Compress files where appropriate.

  • Offload virus scanning to background processing.

  • Store large files in object storage.

  • Monitor storage utilization and upload latency.

Performance optimizations should never compromise upload security.

Security Considerations

Secure file uploads require multiple layers of protection.

Follow these recommendations:

  • Require authentication for protected uploads.

  • Apply authorization policies.

  • Validate every upload.

  • Sanitize metadata.

  • Encrypt sensitive files at rest.

  • Use HTTPS for all uploads.

  • Enable audit logging.

  • Regularly update antivirus definitions.

Security should be enforced throughout the upload lifecycle, from receiving the file to serving it back to authorized users.

Troubleshooting

Uploads Fail for Large Files

Verify the application's maximum request size settings and ensure upload limits are configured consistently across the application and reverse proxy.

Files Cannot Be Downloaded

Confirm that files are stored correctly and that authorized download endpoints resolve the generated file names to the correct storage location.

Duplicate Files Overwrite Existing Content

Use generated unique file names instead of client-provided names to prevent collisions.

Invalid Files Reach Storage

Review the validation pipeline to ensure file size, extension, MIME type, and malware scanning occur before permanent storage.

Conclusion

Secure file uploads require far more than simply accepting an IFormFile and writing it to disk. A production-ready implementation validates file size and type, generates safe file names, stores content securely, scans for malware, and protects downloads through authorization. By following these practices in ASP.NET Core 10, developers can build reliable file upload APIs that scale effectively while minimizing security risks.