Symmetric denial of service - Dart

Symmetric denial of service - Dart

Need

Prevention of service outages caused by repeated resource-intensive requests

Context

  • Usage of Dart 2.0 for developing cross-platform applications
  • Usage of Shelf for ORM (Object-Relational Mapping) in Node.js
  • Usage of shelf_io for managing data storage and retrieval

Description

Non compliant code

        import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as io;

void main() {
  var handler = const shelf.Pipeline().addMiddleware(shelf.logRequests())
      .addHandler(_echoRequest);

  io.serve(handler, 'localhost', 8080);
}

shelf.Response _echoRequest(shelf.Request request) {
  // This is a resource-intensive operation
  for (int i = 0; i < 100000000; i++) {
    print(i);
  }
  return shelf.Response.ok('Request processed.');
}
        
        

The above Dart code represents a simple server application using the shelf framework. The application listens to requests on port 8080 and processes them using the _echoRequest function.

The vulnerability lies in the _echoRequest function where a resource-intensive operation is performed. In this case, the function runs a loop for 100,000,000 iterations and prints each iteration to the console. This operation consumes a lot of system resources and takes a considerable amount of time to complete.

If an attacker repeatedly sends requests to the server, each request will trigger this resource-intensive operation. As a result, the server will be overwhelmed with the processing of these requests and will become unresponsive. This is known as a Symmetric Denial of Service (DoS) vulnerability.

Steps

  • Identify the resource-intensive operations in your code. In this case, it's the loop that prints numbers from 0 to 100000000.
  • Implement rate limiting to prevent a single client from making too many requests in a short period of time. This can be done using middleware in the server.
  • Consider using asynchronous programming to prevent blocking operations. This allows the server to handle other requests while the resource-intensive operation is being processed.
  • Optimize the resource-intensive operation. In this case, the loop that prints numbers could be removed or replaced with a less resource-intensive operation.
  • Implement monitoring and logging to detect unusual activity or repeated requests from a single client. This can help identify potential denial of service attacks.

Compliant code

        import 'dart:async';
import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_rate_limiter/shelf_rate_limiter.dart';

void main() {
  var handler = const shelf.Pipeline()
      .addMiddleware(shelf.logRequests())
      .addMiddleware(rateLimiterMiddleware(maxOperationsPerPeriod: 5, period: Duration(seconds: 10)))
      .addHandler(_echoRequest);

  io.serve(handler, 'localhost', 8080);
}

Future _echoRequest(shelf.Request request) async {
  // This is a resource-intensive operation
  await Future.delayed(Duration(seconds: 2)); // Simulate delay
  return shelf.Response.ok('Request processed.');
}
        
        

The updated code includes several changes to mitigate the risk of a symmetric denial of service attack.

1. Rate Limiting: We've added a rate limiter middleware to the server pipeline. This middleware limits the number of requests a client can make in a given period. In this case, a client can make a maximum of 5 requests every 10 seconds. This prevents a single client from overwhelming the server with too many requests.

2. Asynchronous Programming: The _echoRequest function has been updated to be an asynchronous function. This means that it returns a Future, which allows the server to handle other requests while it's waiting for the resource-intensive operation to complete. In this case, we've simulated a delay with Future.delayed, but in a real-world scenario, this could be a database query, file operation, or other time-consuming task.

3. Optimization: The resource-intensive operation (a loop that prints numbers from 0 to 100000000) has been removed. In a real-world scenario, you would need to identify and optimize any operations that consume a lot of resources or take a long time to complete.

4. Monitoring and Logging: The server uses the logRequests middleware to log all incoming requests. This can help you detect unusual activity or repeated requests from a single client, which could indicate a potential denial of service attack.

References