Letting users upload files to your website sounds simple, until someone tries to upload malware, a massive 5GB dump, or something with a shady file extension. If you're building a C# web app (ASP.NET or ASP.NET Core), you'll want to make sure file uploads are locked down tight.
This guide breaks down how to safely accept file uploads in your C# web application. We'll cover key security practices, provide example code, offer useful context, and link to helpful docs. Let’s go.
Why File Uploads Are Risky
Before diving in, here’s why you can’t just YOLO your way through file uploads:
- Malicious Files: Executables or scripts disguised as images or documents can compromise your server or users.
- Overwriting Files: Attackers may upload files with the same name as a legit file, overwriting important data.
- Large Files: Massive uploads can choke bandwidth and max out disk space, leading to denial of service.
- Path Traversal: Exploiting path manipulation (e.g.,
../../../web.config
) can allow overwriting critical files.
Bottom line: Any file upload endpoint is a potential entry point for bad actors.
Best Practices for Safe File Uploads
1. Restrict File Types
Only allow file types that your app explicitly needs. Relying solely on file extensions is risky since extensions can be spoofed.
You can do this by extracting the files extension using the Path.GetExtension
method and only allowing those extensions that you yourself have hand picked.
var allowedExtensions = new[] { ".jpg", ".png", ".pdf" };
string fileExtension = Path.GetExtension(file.FileName).ToLowerInvariant();
if (!allowedExtensions.Contains(fileExtension)) {
return BadRequest("File type not allowed.");
}
Tips:
- Use a whitelist approach, never blacklist.
- Inspect MIME type and file headers (magic bytes) if you can.
- Consider using a library to detect file signatures.
2. Limit File Size
Don’t let users tank your server with huge uploads. Enforce both server-side and config-level limits.
Lucky for us, we can set the maximum allowable size in the configuration file.
ASP.NET Web Forms / MVC:
<configuration>
<system.web>
<httpRuntime maxRequestLength="4096" /> <!-- size in KB -->
</system.web>
</configuration>
ASP.NET Core:
services.Configure<FormOptions>(options => {
options.MultipartBodyLengthLimit = 5 * 1024 * 1024; // 5MB
});
Key Point: Validate file size in your controller too. Users can bypass config limits with custom tools.
3. Randomize File Names
User-supplied filenames are a no-go. They can cause collisions or be used for traversal attacks. Use random filenames instead:
var fileName = Guid.NewGuid().ToString() + Path.GetExtension(file.FileName);
Pro tip: Add a timestamp or user ID if you need traceability.
4. Use a Safe Directory
Uploads should live outside of your app's executable directory. Never store them in /wwwroot
unless you're deliberately serving them.
var uploadPath = Path.Combine(Environment.CurrentDirectory, "UserUploads");
Directory.CreateDirectory(uploadPath);
You can also use isolated storage or cloud storage solutions like:
These options offer built-in redundancy, scalability, and permissions.
5. Validate the Content
Files should be what they claim to be. Open the file using a relevant parser to confirm it matches expectations.
Example (using ImageSharp):
using var image = Image.Load(file.OpenReadStream());
If the file isn’t a valid image, this will throw an error. Better to fail fast than process junk.
Key Uses:
- Open PDFs with a library like PdfSharp
- Check image integrity with ImageSharp
- Reject files that can't be parsed cleanly
Example (ASP.NET Core)
Here’s how it all comes together:
[HttpPost("upload")]
public async Task<IActionResult> UploadFile(IFormFile file) {
var allowedExtensions = new[] { ".jpg", ".png", ".pdf" };
var ext = Path.GetExtension(file.FileName).ToLowerInvariant();
if (file == null || file.Length == 0)
return BadRequest("No file uploaded.");
if (!allowedExtensions.Contains(ext))
return BadRequest("Invalid file type.");
if (file.Length > 5 * 1024 * 1024) // 5MB
return BadRequest("File too large.");
var newFileName = Guid.NewGuid().ToString() + ext;
var uploadPath = Path.Combine("wwwroot", "uploads");
Directory.CreateDirectory(uploadPath);
var filePath = Path.Combine(uploadPath, newFileName);
using (var stream = new FileStream(filePath, FileMode.Create)) {
await file.CopyToAsync(stream);
}
return Ok("File uploaded successfully.");
}
You can extend this with virus scanning using tools like ClamAV or integrate logging for suspicious activity.
Final Thoughts
File uploads can be safe, but only if you treat them like a potential threat vector. Use strong validation, storage segregation, and fail-safe defaults. Test it. Break it. Fix it.
Walt is a computer scientist, software engineer, startup founder and previous mentor for a coding bootcamp. He has been creating software for the past 20 years.
Last updated on: