Symmetric denial of service - C-Sharp

Symmetric denial of service - C-Sharp

Need

Prevention of repeated request overload causing server unresponsiveness

Context

  • Usage of C# 7.1 for advanced language features and improvements
  • Usage of Microsoft.AspNetCore.Mvc for building web applications using the ASP.NET Core MVC framework
  • Usage of System.Threading.Tasks for asynchronous programming in .NET

Description

Non compliant code

        [HttpPost]
public async Task HeavyProcessing(HeavyDataModel data)
{
    // A resource-intensive task that may take a long time
    await Task.Run(() => DoHeavyProcessing(data));
    return Ok();
}

private void DoHeavyProcessing(HeavyDataModel data)
{
    // Simulating a heavy processing
    for (int i = 0; i < 1000000000; i++)
    {
        // Some heavy processing on the data
    }
}
        
        

The above code represents a Denial of Service (DoS) vulnerability in an ASP.NET Core application. The HeavyProcessing endpoint accepts POST requests and processes a large amount of data, which is simulated by a loop running a billion times.

A malicious user could repeatedly send requests to this endpoint, causing the server to expend a significant amount of resources on processing these requests. As the server becomes more and more overloaded, legitimate requests may be delayed or not processed at all, effectively causing a denial of service.

The server is particularly vulnerable to this attack because it does not limit the number of requests a client can make in a certain period of time, nor does it limit the amount of resources that can be consumed by a single request. This allows a single malicious client to monopolize the server's resources by repeatedly sending resource-intensive requests.

This is a common vulnerability in web applications and can be mitigated by implementing rate limiting, which restricts the number of requests a client can make in a certain period of time, and by optimizing the processing of requests to reduce the amount of resources consumed.

Steps

  • Implement rate limiting to prevent a single client from making too many requests in a short period of time.
  • Consider using a queue system to manage heavy processing tasks. This way, tasks can be processed in the background without blocking the main thread.
  • Optimize the 'DoHeavyProcessing' function to reduce the amount of resources it uses or the time it takes to run.
  • Implement a timeout for processing requests. If a request takes too long to process, it should be terminated to prevent it from blocking other requests.
  • Consider using caching to store the results of heavy processing tasks. This way, if the same request is made again, the server can return the cached result instead of performing the heavy processing again.

Compliant code

        [HttpPost]
[RateLimit(Seconds = 3)] // Rate limiting
public async Task HeavyProcessing(HeavyDataModel data)
{
    // Check if the result is in the cache
    if (_cache.TryGetValue(data, out var cachedResult))
    {
        return Ok(cachedResult);
    }

    // If not, add the task to the queue and return Accepted
    _queue.Enqueue(data);
    return Accepted();
}

private void DoHeavyProcessing(HeavyDataModel data)
{
    var cts = new CancellationTokenSource();
    cts.CancelAfter(TimeSpan.FromSeconds(30)); // Timeout

    try
    {
        // Simulating a heavy processing
        for (int i = 0; i < 1000000000; i++)
        {
            // Some heavy processing on the data
            cts.Token.ThrowIfCancellationRequested();
        }

        // Cache the result
        _cache.Set(data, result);
    }
    catch (OperationCanceledException)
    {
        // Handle the timeout
    }
}
        
        

The updated code includes several measures to prevent a Symmetric Denial of Service attack:

1. Rate Limiting: The RateLimit attribute is used to limit the frequency of requests that a client can make. In this case, a client can only make a request every 3 seconds.

2. Queue System: Instead of processing the heavy task immediately, it is added to a queue. This allows the server to return a response immediately and process the task in the background.

3. Caching: Before processing a task, the server checks if the result is already in the cache. If it is, the server can return the cached result instead of performing the heavy processing again.

4. Timeout: A CancellationTokenSource is used to implement a timeout for the heavy processing. If the processing takes longer than 30 seconds, an OperationCanceledException is thrown and the processing is stopped.

These measures help to ensure that the server remains responsive even when faced with resource-intensive tasks or repeated requests.

References