Skip to main content

Securing File Uploads Part 2: File Size Validation

In the first post of this series, we explored how content type validation serves as the first line of defense against malicious file uploads. Today, we're tackling another critical security concern: file size validation and why it's essential for protecting your application from resource exhaustion attacks.

The threat: Death by a thousand uploads

File size validation might seem like a simple feature requirement, but it's actually a crucial security control. Without proper size limits, attackers can:

  • Exhaust disk space: Fill up your storage with massive files, causing system failures
  • Consume bandwidth: Drain network resources by uploading gigantic files repeatedly
  • Trigger out-of-memory errors: Crash your application by forcing it to process files larger than available memory
  • Enable denial-of-service attacks: Tie up server resources processing oversized files, preventing legitimate users from accessing your application
  • Inflate storage costs: In cloud environments where storage is billed per gigabyte, unrestricted uploads can lead to unexpectedly high costs

A single malicious actor uploading even a few multi-gigabyte files can bring down an unprotected application or rack up substantial cloud storage bills.

Our implementation: Simple but effective

File size validation is the first step in our validation pipeline (before content type checking) because there's no point in performing expensive validation operations on files that are too large anyway.

Here's our implementation:

Design decisions explained

1. Configurable limits

The maximum file size is injected as a constructor parameter rather than being hardcoded. This allows different parts of your application to have different size limits based on use case. For example:

2. Early validation

File size is checked first in the pipeline because:

  • It's a fast, cheap operation (just reading a property)
  • It prevents wasting resources on oversized files that would fail anyway
  • It stops potential attacks before expensive operations like virus scanning occur

Looking at our pipeline configuration from the previous post:

Why IFormFile.Length is safe to use

You might wonder if checking file.Length is safe—couldn't an attacker manipulate this value? The good news is that IFormFile.Length represents the actual uploaded content length as determined by ASP.NET Core, not a user-supplied header. The framework calculates this from the actual received data.

However, there's an important caveat: this validation only happens after the file has been uploaded to the server (either to memory or a temporary file). To truly prevent resource exhaustion, you need defense in depth.

Defense in depth: Multiple layers of protection

Our FileSizeValidationStep works in conjunction with requests size limits in ASP.NET Core:

And at the IIS level (in our case):

What's next

File size validation protects against resource exhaustion, but it doesn't address the content of the files themselves. In the next post, we'll explore file signature validation—the technique that ensures files are actually what they claim to be, regardless of their extension or content type header.

Popular posts from this blog

.NET 8–Keyed/Named Services

A feature that a lot of IoC container libraries support but that was missing in the default DI container provided by Microsoft is the support for Keyed or Named Services. This feature allows you to register the same type multiple times using different names, allowing you to resolve a specific instance based on the circumstances. Although there is some controversy if supporting this feature is a good idea or not, it certainly can be handy. To support this feature a new interface IKeyedServiceProvider got introduced in .NET 8 providing 2 new methods on our ServiceProvider instance: object? GetKeyedService(Type serviceType, object? serviceKey); object GetRequiredKeyedService(Type serviceType, object? serviceKey); To use it, we need to register our service using one of the new extension methods: Resolving the service can be done either through the FromKeyedServices attribute: or by injecting the IKeyedServiceProvider interface and calling the GetRequiredKeyedServic...

Azure DevOps/ GitHub emoji

I’m really bad at remembering emoji’s. So here is cheat sheet with all emoji’s that can be used in tools that support the github emoji markdown markup: All credits go to rcaviers who created this list.

Kubernetes–Limit your environmental impact

Reducing the carbon footprint and CO2 emission of our (cloud) workloads, is a responsibility of all of us. If you are running a Kubernetes cluster, have a look at Kube-Green . kube-green is a simple Kubernetes operator that automatically shuts down (some of) your pods when you don't need them. A single pod produces about 11 Kg CO2eq per year( here the calculation). Reason enough to give it a try! Installing kube-green in your cluster The easiest way to install the operator in your cluster is through kubectl. We first need to install a cert-manager: kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.5/cert-manager.yaml Remark: Wait a minute before you continue as it can take some time before the cert-manager is up & running inside your cluster. Now we can install the kube-green operator: kubectl apply -f https://github.com/kube-green/kube-green/releases/latest/download/kube-green.yaml Now in the namespace where we want t...