ClamAV Antivirus Scan On File Upload Content In .NET Application

Introduction

Most ASP.NET Web applications have file upload functionality where users can upload files and those files are saved on a server or blob storage. But without performing anti-virus scanning or other content checking against the uploaded file, attackers could target other users of the application by uploading malware to the server.

The application should perform filtering and content checking on any files that are uploaded to the server. Files should be thoroughly scanned and validated against an antivirus scanner with up-to-date virus signatures before being made available to other users. Any files flagged as malicious should be discarded or deleted immediately.

To achieve this, we will use an open-source antivirus engine called ClamAV via nClam NuGet package, a pure .NET client to ClamAV.

This article demonstrates how we can perform secure and effective file scanning using ClamAV antivirus.

Prerequisites

The ClamAV server should be up and running on a specific port. In case, you want to run the ClamAV server in localhost, you can follow the subsequent steps below.

Running ClamAV on localhost using Docker Image

This step is optional if you already have the ClamAV server. Here I used the docker image as the simplest and easiest way to run it.

ClamAV demon is a Dockerized open-source antivirus image. Let’s follow below simple steps below to install and run on localhost via default TCP port 3310. I am assuming you have Docker Desktop's latest version installed and running on your PC.

Execute the below commands from the command prompt.

docker run -d -p 3310:3310 mkodockx/docker-clamav:alpine
docker run -d -p 3310:3310 mk0x/docker-clamav
docker ps

Command

The ClamAV Server is now set up!

For more details, you can check here.

Implementing ClamAV antivirus scan on uploaded files in .NET application

Now we are ready to run on-demand scans and anything from code using nClam. To demonstrate an example, here I created a simple file upload UI in ASP.NET Core MVC Web application (you can have a similar ASP.NET website in WebForms or MVC with file upload input) where we will browse and upload different types of files like PDF, IMAGE, ZIP, DCOX, etc.

The Approach

  1. Install the nClam NuGet package.
  2. Add ClamAV server and port to the config file.
  3. Read file from File upload and convert to byte array.
  4. Scan file byte array against ClamClient and validate ClamScanResult object.
  5. Test ClamAV scan against a valid file
  6. Test ClamAV scan against an Anti Malware test file

Step 1. Install nClam NuGet package

Install below NuGet package from the NuGet package manager into your project.

<PackageReference Include="nClam" Version="4.0.1" />

Step 2. Add ClamAV server and port to appsettings.json.

{
  "ClamAVServer": {
    "URL": "localhost",
    "Port": "3310"
  }
}

Step 3. Read a file from File upload and convert to byte array.

Step 4. Scan file byte array against ClamClient and validate ClamScanResult object

ClamClient scan result returns with ClamScanResult enum values which tell you if your scan was clean or a virus was detected. Here is some sample code.

private readonly ILogger<HomeController> _logger;  
private readonly IConfiguration _configuration;  

public HomeController(ILogger<HomeController> logger, IConfiguration configuration)  
{  
    _logger = logger;  
    _configuration = configuration;  
}  

[HttpPost]  
public async Task<IActionResult> UploadFile(IFormFile file)  
{  
    if (file == null || file.Length == 0)
        return Content("file not selected");  

    var ms = new MemoryStream();  
    file.OpenReadStream().CopyTo(ms);  
    byte[] fileBytes = ms.ToArray();  
    
    try  
    {  
        _logger.LogInformation("ClamAV scan begin for file {0}", file.FileName);  
        var clam = new ClamClient(_configuration["ClamAVServer:URL"],    
                                  Convert.ToInt32(_configuration["ClamAVServer:Port"]));  
        var scanResult = await clam.SendAndScanFileAsync(fileBytes);    
        switch (scanResult.Result)  
        {  
            case ClamScanResults.Clean:  
                _logger.LogInformation("The file is clean! ScanResult: {1}", scanResult.RawResult);  
                break;  
            case ClamScanResults.VirusDetected:  
                _logger.LogError("Virus Found! Virus name: {1}", scanResult.InfectedFiles.FirstOrDefault().VirusName);  
                break;  
            case ClamScanResults.Error:  
                _logger.LogError("An error occurred while scanning the file! ScanResult: {1}", scanResult.RawResult);  
                break;  
            case ClamScanResults.Unknown:  
                _logger.LogError("Unknown scan result while scanning the file! ScanResult: {0}", scanResult.RawResult);  
                break;  
        }  
    }  
    catch (Exception ex)  
    {  
        _logger.LogError("ClamAV Scan Exception: {0}", ex.ToString());  
    }  
    
    _logger.LogInformation("ClamAV scan completed for file {0}", file.FileName);  
    return RedirectToAction("Index");  
}

In case you have file content in base 64 strings, then convert it to a byte array and send the same byte array to ClamClient for scanning.

Step 5. Test ClamAV scan against a valid file.

Now we are ready to test our code against a valid PDF and images.

 PDF

File Upload

Step 6. Test ClamAV scan against an Anti Malware test file.

As a POC, the EICAR file was uploaded. This is a sample file used to test the response of anti-virus software. You can download a sample file from https://www.eicar.org/?page_id=3950. You may need to pause your antivirus protection on your device to perform this activity.

 Test file

Count

The below information is from the application console log,

Console log

Conclusion

In this article, we have seen how to run ClamAV in localhost using a docker image and implemented and tested an antivirus scan with a valid file and a virus-infected file. The application identified the upload of this file with the scan result, now you can add your logic to remove the file. Hope you found this information useful! Sharing is caring.