Skip to main content

Securing File Uploads Part 4: Malware Scanning with Windows AMSI

Welcome to the final post in our file upload security series. We've covered content type validation, file size validation, and file signature validation—each providing a crucial layer of defense. Today, we're implementing the final and most sophisticated protection: malware scanning using Windows Antimalware Scan Interface (AMSI).

The last line of defense

Even after all our previous validation steps, a determined attacker could still upload malicious content:

  • A legitimate PDF with embedded JavaScript exploits
  • A valid Office document containing malicious macros
  • An actual image file with embedded steganographic payloads
  • A genuine archive containing malware
  • Zero-day exploits targeting file processing libraries

These files pass all our previous validations because they are legitimate file formats—they're just weaponized. This is where malware scanning becomes essential.

Why AMSI?

Windows Antimalware Scan Interface (AMSI) is a powerful, often overlooked Windows feature that allows applications to integrate with the system's antivirus engine. Here's why it's an excellent choice:

Advantages:

  • Leverages existing antivirus: Uses Windows Defender or any AMSI-compatible antivirus already installed
  • Always up-to-date: Detection signatures are updated automatically by Windows Update
  • No additional licensing: Free to use on Windows systems
  • Lightweight integration: Simple API with minimal overhead
  • Industry-standard detection: Benefits from Microsoft's extensive threat intelligence
  • Content-based scanning: Analyzes actual file content, not just signatures

Limitations:

  • Windows-only: AMSI is exclusive to Windows (for cross-platform needs, consider ClamAV)
  • Requires Windows Defender or compatible AV: Must have an AMSI-compatible antivirus engine installed
  • Not foolproof: Like all antivirus solutions, it can't detect unknown zero-day threats

For applications running on Windows Server, AMSI provides enterprise-grade malware detection without additional infrastructure or licensing costs.

Our implementation

Here's our malware scanning validation step:

Integrating AMSI

The AMSI integration code is based on Gérald Barré's excellent article. Here's the core implementation you'll need:

AMSI scans are typically fast (100-500ms for most files), but they can take longer for:

  • Very large files
  • Archives with many nested files
  • Files with complex structure (Office documents)
  • First scan after Windows Defender updates

The complete validation pipeline

Here's how all four validation steps work together for one last time:

Testing Strategy

Testing malware scanning is more complex than our previous validation steps. We used the EICAR test file approach.

The EICAR test file is a standard anti-malware test file that's detected by all major antivirus engines as malware, but is completely harmless:

Conclusion: Defense in depth works

Over this four-part series, we've built a comprehensive file upload security pipeline:

  • Part 1: Content type validation stopped files with wrong MIME types
  • Part 2: Size validation prevented resource exhaustion attacks
  • Part 3: Signature validation caught files masquerading as other types
  • Part 4: Malware scanning detected weaponized legitimate files

No single validation step is sufficient on its own. But together, they create overlapping layers of defense that have proven remarkably effective.

File upload security is not a "set it and forget it" concern. It requires ongoing vigilance, monitoring, and adaptation as new threats emerge. But with the foundation we've built, you're well-equipped to protect your applications from the vast majority of file upload attacks.

More information

Antimalware Scan Interface (AMSI) - Win32 apps | Microsoft Learn

Using Windows Antimalware Scan Interface in .NET - Meziantou's blog

EICAR test file - Wikipedia

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...