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:
A user selects a document.
The API validates file size.
The extension and MIME type are verified.
A unique file name is generated.
The file is scanned for malware.
The file is stored outside the web root.
File metadata is stored in the database.
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:
Receive upload.
Store temporarily.
Scan using antivirus software.
Delete infected files.
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 Option | Advantages | Considerations |
|---|
| Local Disk | Simple setup | Limited scalability |
| Network Storage | Shared access | Additional infrastructure |
| Azure Blob Storage | Highly scalable | Cloud dependency |
| Amazon S3 | Durable and scalable | Requires 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:
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.