Description
Failing to validate uploaded files or serving their contents as HTML can allow attackers to upload files containing scripts and have those scripts executed.
Potential impact
- Stored XSS, distribution of malicious files, and content-type confusion.
Remediation
Validate file size, extension, MIME type, and storage location. Serve uploaded files with a safe content type.
Examples
Before
csharp
using Microsoft.AspNetCore.Mvc;
public sealed class UploadController : Controller
{
public IActionResult View(string id)
{
var fileBytes = System.IO.File.ReadAllBytes("/tmp/uploads/" + id);
return File(fileBytes, "text/html");
}
}
After
csharp
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.IO;
using System.Threading.Tasks;
public sealed class UploadController : Controller
{
public async Task<IActionResult> Upload([FromForm] IFormFile file)
{
if (file.Length is <= 0 or > 1_048_576) return BadRequest();
if (!string.Equals(
Path.GetExtension(file.FileName), ".txt",
StringComparison.OrdinalIgnoreCase)) return BadRequest();
// Only the service must be able to write to this path and its parent directories.
var uploadRoot = Path.GetFullPath("/var/lib/app/uploads");
Directory.CreateDirectory(uploadRoot);
var storedName = $"{Guid.NewGuid():N}.txt";
var destination = Path.Combine(uploadRoot, storedName);
await using var output = new FileStream(
destination, FileMode.CreateNew, FileAccess.Write, FileShare.None);
await file.CopyToAsync(output);
return Accepted(new { id = storedName });
}
}
Explanation:
- Before: Storing uploaded files without validation and then serving them as HTML can execute uploaded scripts in the browser.
- After: Limit size and allowed extensions, use a server-generated name instead of the client filename, and create only new files outside the web root. In a real service, also validate the file's actual contents and scan for malware. Do not serve downloads with an executable HTML MIME type.